add car racing sim and first geno to phenotype mapper experiment.

This commit is contained in:
2025-09-24 14:15:18 +02:00
parent 581109117c
commit 86c67b66f8
17 changed files with 1015 additions and 2 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12 (ba)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (neuroevolution)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/neuroevolution.iml" filepath="$PROJECT_DIR$/.idea/neuroevolution.iml" />
</modules>
</component>
</project>

14
.idea/neuroevolution.iml generated Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 (neuroevolution)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,12 @@
import gymnasium as gym
env = gym.make("CarRacing-v3", render_mode="human")
obs, info = env.reset()
for _ in range(1000):
action = env.action_space.sample()
_, reward, terminated, truncated, _ = env.step(action)
if terminated or truncated:
obs, info = env.reset()
env.close()

View File

@@ -6,7 +6,7 @@ from typing import List, Dict, Tuple
# ---- Hilfsfunktionen ---- # ---- Hilfsfunktionen ----
def generate_id(): def generate_id():
"""Generiert eine eindeutige ID basierend auf Zufallszahlen.""" """generate random number as id. TODO: this should be uuidv4 instead of float"""
return random.random() return random.random()

View File

@@ -0,0 +1,258 @@
{
"cortex": {
"id": 0.38985127736117664,
"sensor_ids": [
0.5481697495393968
],
"actuator_ids": [
0.300862433948894
],
"neuron_ids": [
0.7892266733003815,
0.08993126065168999,
0.7353679413901013,
0.7986671199471203,
0.2619274118215478,
0.1878355172703,
0.5460586524022121,
0.0325156080072021
]
},
"sensor": {
"id": 0.5481697495393968,
"name": "rng",
"vector_length": 2,
"cx_id": 0.37250552293263184,
"fanout_ids": [
0.7892266733003815,
0.08993126065168999,
0.7353679413901013,
0.7986671199471203
]
},
"actuator": {
"id": 0.300862433948894,
"name": "pts",
"vector_length": 1,
"cx_id": 0.37250552293263184,
"fanin_ids": [
0.0325156080072021
]
},
"neurons": [
{
"id": 0.7892266733003815,
"layer_index": 0,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.5481697495393968,
"weights": [
-0.2010596737648036,
-0.017559575650012982
]
}
],
"output_ids": [
0.11787440750922895,
0.9473188607259506,
0.9045741659912035
]
},
{
"id": 0.08993126065168999,
"layer_index": 0,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.5481697495393968,
"weights": [
0.49425647147649876,
0.09556856915703738
]
}
],
"output_ids": [
0.11787440750922895,
0.9473188607259506,
0.9045741659912035
]
},
{
"id": 0.7353679413901013,
"layer_index": 0,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.5481697495393968,
"weights": [
-0.3432015569352376,
-0.15663876804924903
]
}
],
"output_ids": [
0.11787440750922895,
0.9473188607259506,
0.9045741659912035
]
},
{
"id": 0.7986671199471203,
"layer_index": 0,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.5481697495393968,
"weights": [
0.44235413103542676,
-0.3014661028473905
]
}
],
"output_ids": [
0.11787440750922895,
0.9473188607259506,
0.9045741659912035
]
},
{
"id": 0.2619274118215478,
"layer_index": 1,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.7892266733003815,
"weights": [
0.4815260544600901
]
},
{
"input_id": 0.08993126065168999,
"weights": [
-0.34742595611872107
]
},
{
"input_id": 0.7353679413901013,
"weights": [
0.1955465593022997
]
},
{
"input_id": 0.7986671199471203,
"weights": [
0.1420046463445398
]
}
],
"output_ids": [
0.804289241732289
]
},
{
"id": 0.1878355172703,
"layer_index": 1,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.7892266733003815,
"weights": [
-0.26492297905129614
]
},
{
"input_id": 0.08993126065168999,
"weights": [
-0.483071194536965
]
},
{
"input_id": 0.7353679413901013,
"weights": [
0.0016581996680702371
]
},
{
"input_id": 0.7986671199471203,
"weights": [
0.47010344086613354
]
}
],
"output_ids": [
0.804289241732289
]
},
{
"id": 0.5460586524022121,
"layer_index": 1,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.7892266733003815,
"weights": [
0.22583100100162312
]
},
{
"input_id": 0.08993126065168999,
"weights": [
0.22079487426614341
]
},
{
"input_id": 0.7353679413901013,
"weights": [
0.3514718848950448
]
},
{
"input_id": 0.7986671199471203,
"weights": [
0.03653443587296967
]
}
],
"output_ids": [
0.804289241732289
]
},
{
"id": 0.0325156080072021,
"layer_index": 2,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.2619274118215478,
"weights": [
0.4397546856440344
]
},
{
"input_id": 0.1878355172703,
"weights": [
0.024524621225712195
]
},
{
"input_id": 0.5460586524022121,
"weights": [
0.19908558570374346
]
}
],
"output_ids": [
0.300862433948894
]
}
]
}

View File

@@ -0,0 +1,258 @@
{
"cortex": {
"id": 0.38985127736117664,
"sensor_ids": [
0.5481697495393968
],
"actuator_ids": [
0.300862433948894
],
"neuron_ids": [
0.7892266733003815,
0.08993126065168999,
0.7353679413901013,
0.7986671199471203,
0.2619274118215478,
0.1878355172703,
0.5460586524022121,
0.0325156080072021
]
},
"sensor": {
"id": 0.5481697495393968,
"name": "rng",
"vector_length": 2,
"cx_id": 0.37250552293263184,
"fanout_ids": [
0.7892266733003815,
0.08993126065168999,
0.7353679413901013,
0.7986671199471203
]
},
"actuator": {
"id": 0.300862433948894,
"name": "pts",
"vector_length": 1,
"cx_id": 0.37250552293263184,
"fanin_ids": [
0.0325156080072021
]
},
"neurons": [
{
"id": 0.7892266733003815,
"layer_index": 0,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.5481697495393968,
"weights": [
-0.2010596737648036,
-0.017559575650012982
]
}
],
"output_ids": [
0.11787440750922895,
0.9473188607259506,
0.9045741659912035
]
},
{
"id": 0.08993126065168999,
"layer_index": 0,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.5481697495393968,
"weights": [
0.49425647147649876,
0.09556856915703738
]
}
],
"output_ids": [
0.11787440750922895,
0.9473188607259506,
0.9045741659912035
]
},
{
"id": 0.7353679413901013,
"layer_index": 0,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.5481697495393968,
"weights": [
-0.3432015569352376,
-0.15663876804924903
]
}
],
"output_ids": [
0.11787440750922895,
0.9473188607259506,
0.9045741659912035
]
},
{
"id": 0.7986671199471203,
"layer_index": 0,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.5481697495393968,
"weights": [
0.44235413103542676,
-0.3014661028473905
]
}
],
"output_ids": [
0.11787440750922895,
0.9473188607259506,
0.9045741659912035
]
},
{
"id": 0.2619274118215478,
"layer_index": 1,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.7892266733003815,
"weights": [
0.4815260544600901
]
},
{
"input_id": 0.08993126065168999,
"weights": [
-0.34742595611872107
]
},
{
"input_id": 0.7353679413901013,
"weights": [
0.1955465593022997
]
},
{
"input_id": 0.7986671199471203,
"weights": [
0.1420046463445398
]
}
],
"output_ids": [
0.804289241732289
]
},
{
"id": 0.1878355172703,
"layer_index": 1,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.7892266733003815,
"weights": [
-0.26492297905129614
]
},
{
"input_id": 0.08993126065168999,
"weights": [
-0.483071194536965
]
},
{
"input_id": 0.7353679413901013,
"weights": [
0.0016581996680702371
]
},
{
"input_id": 0.7986671199471203,
"weights": [
0.47010344086613354
]
}
],
"output_ids": [
0.804289241732289
]
},
{
"id": 0.5460586524022121,
"layer_index": 1,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.7892266733003815,
"weights": [
0.22583100100162312
]
},
{
"input_id": 0.08993126065168999,
"weights": [
0.22079487426614341
]
},
{
"input_id": 0.7353679413901013,
"weights": [
0.3514718848950448
]
},
{
"input_id": 0.7986671199471203,
"weights": [
0.03653443587296967
]
}
],
"output_ids": [
0.804289241732289
]
},
{
"id": 0.0325156080072021,
"layer_index": 2,
"cx_id": 0.37250552293263184,
"activation_function": "tanh",
"input_weights": [
{
"input_id": 0.2619274118215478,
"weights": [
0.4397546856440344
]
},
{
"input_id": 0.1878355172703,
"weights": [
0.024524621225712195
]
},
{
"input_id": 0.5460586524022121,
"weights": [
0.19908558570374346
]
}
],
"output_ids": [
0.300862433948894
]
}
]
}

View File

@@ -0,0 +1,410 @@
import asyncio
import json
import math
import random
from collections import defaultdict
from typing import Dict, List, Tuple, Any
# utils
def tanh(x: float) -> float:
return math.tanh(x)
def rng_vector(vl: int) -> List[float]:
# wie sensor:rng(VL)
return [random.random() for _ in range(vl)]
# actor base class
class Actor:
"""
base class for all our actors: sensors, actuators, neurons, cortex.
"""
def __init__(self, name: str):
self.name = name
self.inbox: asyncio.Queue = asyncio.Queue()
async def send(self, msg: Tuple[Any, ...]):
"""
"""
await self.inbox.put(msg)
async def run(self):
"""
this needs to be overridden in subclasses.
"""
raise NotImplementedError
# sensor
class Sensor(Actor):
def __init__(self, sid: Any, cx_pid: "Actor", name: str, vl: int, fanout_pids: List["Actor"]):
super().__init__(f"Sensor-{sid}")
self.sid = sid
self.cx_pid = cx_pid
self.sname = name
self.vl = vl
self.fanout = fanout_pids
async def run(self):
while True:
msg = await self.inbox.get()
tag = msg[0]
if tag == "sync": # {Cx, sync}
# if there is a sync command, we sense from the environment
# and put the result in the inbox of the neurons connected to sensor.
print("sensing...")
# choose sensor funtion
if self.sname == "rng":
vec = rng_vector(self.vl)
else:
# place for own sensors (this will be replaced by scapes down the road)
vec = [0.0] * self.vl
# forward an alle Fanouts
for pid in self.fanout:
await pid.send(("forward", self.sid, vec))
elif tag == "terminate":
# terminate.
return
# neuron
class Neuron(Actor):
def __init__(self, nid: Any, cx_pid: "Actor", af_name: str,
input_idps: List[Tuple[Any, List[float]]], # [(input_id, weights)] (+ optional bias am Ende)
output_pids: List["Actor"]):
super().__init__(f"Neuron-{nid}")
self.nid = nid
self.cx_pid = cx_pid
self.af = tanh if af_name == "tanh" else tanh
# input-id → (weights, received_flag, last_value_vector_or_scalar)
self.inputs: Dict[Any, Dict[str, Any]] = {}
self.order: List[Any] = [] # for deterministic order
for entry in input_idps:
self.order.append(entry[0])
self.inputs[entry[0]] = {"weights": entry[1], "got": False, "val": None}
self.bias: float = 0.0 # optional bias
self.outputs = output_pids
async def run(self):
while True:
msg = await self.inbox.get()
tag = msg[0]
if tag == "forward":
# ("forward", from_id, value_vec_or_scalar)
_tag, from_id, data = msg
if from_id not in self.inputs:
# unexpected source - we continue and ignore it
continue
# we mark that we have values from this source to keep sequence
self.inputs[from_id]["got"] = True
self.inputs[from_id]["val"] = data
# when we have a signal from all sources: calculate dot product.
if all(self.inputs[i]["got"] for i in self.order):
acc = 0.0
for i in self.order:
w = self.inputs[i]["weights"]
v = self.inputs[i]["val"]
# first layer: v is vector -> dotproduct
if isinstance(v, list):
acc += sum(wj * vj for wj, vj in zip(w, v))
else:
# from layer 1: scalar -> 1 weight
acc += w[0] * float(v)
out = self.af(acc + self.bias)
# an alle Outputs als Vektor (wie Erlang: [Output])
for pid in self.outputs:
await pid.send(("forward", self.nid, [out]))
# reset "got"-flags so the next cycle can start
for i in self.order:
self.inputs[i]["got"] = False
self.inputs[i]["val"] = None
elif tag == "get_backup":
# ("get_backup", reply_to)
_tag, reply_to = msg
# we create (nid, [(input_id, weights), ..., ('bias', bias)])
idps = [(i, self.inputs[i]["weights"]) for i in self.order]
idps.append(("bias", self.bias))
# send back to cortex
await reply_to.send(("backup_from_neuron", self.nid, idps))
elif tag == "terminate":
return
# ---------- Actuator ----------
class Actuator(Actor):
def __init__(self, aid: Any, cx_pid: "Actor", name: str, fanin_ids: List[Any], expect_count: int):
super().__init__(f"Actuator-{aid}")
self.aid = aid
self.cx_pid = cx_pid
self.aname = name
self.fanin_ids = fanin_ids[:] # order matters!
self.expect = expect_count
self.received: Dict[Any, List[float]] = {}
async def run(self):
while True:
msg = await self.inbox.get()
tag = msg[0]
if tag == "forward":
_tag, from_id, vec = msg
self.received[from_id] = vec
if len(self.received) == self.expect:
# collect in order of fanin_ids
result: List[float] = []
for fid in self.fanin_ids:
result.extend(self.received[fid])
# actuator functions (here: pts → print)
if self.aname == "pts":
print(f"actuator:pts(Result): {result}")
# send sync message to cortex
await self.cx_pid.send(("sync_from_actuator", self.aid))
self.received.clear()
elif tag == "terminate":
return
# cortex with report queue
class Cortex(Actor):
def __init__(self, cid: Any, sensor_pids: List["Actor"], actuator_pids: List["Actor"],
neuron_pids: List["Actor"], total_steps: int, report_queue: asyncio.Queue):
super().__init__(f"Cortex-{cid}")
self.cid = cid
self.sensors = sensor_pids
self.actuators = actuator_pids
self.neurons = neuron_pids
self.total_steps = total_steps
self.awaiting_sync: set = set()
self.actuator_ids: List[Any] = []
self.report_queue = report_queue
async def run(self):
# start: trigger all sensors
for s in self.sensors:
await s.send(("sync",))
while True:
msg = await self.inbox.get()
tag = msg[0]
if tag == "register_actuators":
_, actuator_ids = msg
self.actuator_ids = list(actuator_ids)
self.awaiting_sync = set(self.actuator_ids)
elif tag == "sync_from_actuator":
_, aid = msg
if aid in self.awaiting_sync:
self.awaiting_sync.remove(aid)
if not self.awaiting_sync:
self.total_steps -= 1
if self.total_steps <= 0:
# collect backup
weights = []
for n in self.neurons:
await n.send(("get_backup", self))
remaining = len(self.neurons)
while remaining > 0:
bmsg = await self.inbox.get()
if bmsg[0] == "backup_from_neuron":
_, nid, idps = bmsg
weights.append((nid, idps))
remaining -= 1
# report result to exoself
await self.report_queue.put(("completed_backup", weights))
# terminate
for s in self.sensors:
await s.send(("terminate",))
for a in self.actuators:
await a.send(("terminate",))
for n in self.neurons:
await n.send(("terminate",))
return
# Nächster Zyklus
self.awaiting_sync = set(self.actuator_ids)
for s in self.sensors:
await s.send(("sync",))
elif tag == "completed_backup":
# placeholder
pass
# exoself
class Exoself:
def __init__(self, genotype: Dict[str, Any], total_steps: int = 10):
self.g = genotype
self.total_steps = total_steps
self.cx_actor: Cortex = None
self.pid_by_id: Dict[Any, Actor] = {}
self.report_queue: asyncio.Queue = asyncio.Queue()
# --- helpers for arrays of sensors and actuators ---
@staticmethod
def _as_list(maybe_list_or_item):
if maybe_list_or_item is None:
return []
if isinstance(maybe_list_or_item, list):
return maybe_list_or_item
return [maybe_list_or_item]
def _get_sensors_json(self) -> List[Dict[str, Any]]:
if "sensors" in self.g and isinstance(self.g["sensors"], list):
return self.g["sensors"]
elif "sensor" in self.g and isinstance(self.g["sensor"], dict):
return [self.g["sensor"]]
else:
return []
def _get_actuators_json(self) -> List[Dict[str, Any]]:
if "actuators" in self.g and isinstance(self.g["actuators"], list):
return self.g["actuators"]
elif "actuator" in self.g and isinstance(self.g["actuator"], dict):
return [self.g["actuator"]]
else:
return []
def _build_pid_map(self):
cx = self.g["cortex"]
self.cx_actor = Cortex(cx["id"], [], [], [], self.total_steps, self.report_queue)
self.pid_by_id[cx["id"]] = self.cx_actor
# placeholder for all known ids
for s in self._get_sensors_json():
self.pid_by_id[s["id"]] = None
for a in self._get_actuators_json():
self.pid_by_id[a["id"]] = None
for n in self.g["neurons"]:
self.pid_by_id[n["id"]] = None
def _link_and_spawn(self):
# order neurons by layers
layers = defaultdict(list)
for n in self.g["neurons"]:
layers[n["layer_index"]].append(n)
ordered_layers = [layers[i] for i in sorted(layers)]
if not ordered_layers:
raise ValueError("Keine Neuronen im Genotyp gefunden.")
# build actuators
actuators_json = self._get_actuators_json()
actuator_pid_by_id: Dict[Any, Actuator] = {}
# for last layer: we need list of neuron-ids (for matching)
last_layer = ordered_layers[-1]
last_layer_ids = {n["id"] for n in last_layer}
for a in actuators_json:
fanin_ids = self._as_list(a.get("fanin_ids", []))
# Safety: if genotype is empty at this place, take neurons of last layer
if not fanin_ids:
fanin_ids = list(last_layer_ids)
actuator = Actuator(a["id"], self.cx_actor, a["name"], fanin_ids, expect_count=len(fanin_ids))
self.pid_by_id[a["id"]] = actuator
actuator_pid_by_id[a["id"]] = actuator
actuator_actors = list(actuator_pid_by_id.values())
# build the neurons
neuron_pid_by_id: Dict[Any, Neuron] = {}
for layer in ordered_layers:
for n in layer:
input_idps = [(iw["input_id"], iw["weights"]) for iw in n["input_weights"]]
neuron = Neuron(n["id"], self.cx_actor, n["activation_function"], input_idps, [])
neuron_pid_by_id[n["id"]] = neuron
self.pid_by_id[n["id"]] = neuron
# set next layer as output for non output neurons
for li, layer in enumerate(ordered_layers[:-1]):
next_layer = ordered_layers[li + 1]
next_pids = [neuron_pid_by_id[nx["id"]] for nx in next_layer]
for n in layer:
neuron_pid_by_id[n["id"]].outputs = next_pids
# outputs for output layer: actuators
for n in ordered_layers[-1]:
outs: List[Actor] = []
nid = n["id"]
for a in actuators_json:
fanin_ids = set(self._as_list(a.get("fanin_ids", [])))
# default: if fanin empty, all last layer feed this actuator
if not fanin_ids or nid in fanin_ids:
outs.append(actuator_pid_by_id[a["id"]])
neuron_pid_by_id[nid].outputs = outs
# build sensors
sensors_json = self._get_sensors_json()
sensor_actors: List[Sensor] = []
first_layer = ordered_layers[0]
first_layer_pid_by_id = {n["id"]: neuron_pid_by_id[n["id"]] for n in first_layer}
for s in sensors_json:
# if the genotype contains fanout_ids we use exactly these
fanout_ids = self._as_list(s.get("fanout_ids", []))
if fanout_ids:
fanout_pids = [first_layer_pid_by_id[fid] for fid in fanout_ids if fid in first_layer_pid_by_id]
# if fanout_ids point to non-first-layer (unclean genotype), fallback:
if not fanout_pids:
fanout_pids = list(first_layer_pid_by_id.values())
else:
# default: all neurons in first layer
fanout_pids = list(first_layer_pid_by_id.values())
sensor = Sensor(s["id"], self.cx_actor, s["name"], s["vector_length"], fanout_pids)
self.pid_by_id[s["id"]] = sensor
sensor_actors.append(sensor)
# fill cortex
self.cx_actor.sensors = sensor_actors
self.cx_actor.actuators = actuator_actors
self.cx_actor.neurons = [neuron_pid_by_id[n["id"]] for n in self.g["neurons"]]
async def run(self) -> List[Tuple[Any, List[Tuple[Any, List[float]]]]]:
self._build_pid_map()
self._link_and_spawn()
# start tasks
tasks = [asyncio.create_task(self.cx_actor.run())]
for v in self.pid_by_id.values():
if isinstance(v, Actor) and v is not self.cx_actor:
tasks.append(asyncio.create_task(v.run()))
# register all actuator ids
actuator_ids = [a["id"] for a in self._get_actuators_json()]
await self.cx_actor.send(("register_actuators", actuator_ids))
# wait for result in report queue
tag, weights = await self.report_queue.get()
assert tag == "completed_backup"
# cleanup
for t in tasks:
if not t.done():
t.cancel()
return weights
@staticmethod
def update_genotype_with_backup(geno: Dict[str, Any],
backup: List[Tuple[Any, List[Tuple[Any, List[float]]]]]) -> Dict[str, Any]:
by_id = {n["id"]: n for n in geno["neurons"]}
for nid, idps in backup:
if nid not in by_id:
continue
new_iw = []
for item in idps:
if isinstance(item[0], str) and item[0] == "bias":
continue
input_id, weights = item
new_iw.append({"input_id": input_id, "weights": weights})
by_id[nid]["input_weights"] = new_iw
return geno

View File

@@ -0,0 +1,19 @@
import asyncio
import json
from pehno_geno_map import Exoself
# load genotype from json
with open("genotype.json") as f:
geno = json.load(f)
# map phenotype and run for 5 cycles
ex = Exoself(geno, total_steps=5)
# get backup
backup = asyncio.run(ex.run())
print("Backup erhalten (gekürzt):", [(nid, len(idps)) for nid, idps in backup])
# update genotype and safe
u_geno = Exoself.update_genotype_with_backup(geno, backup)
with open("genotype_updated.json", "w") as f:
json.dump(u_geno, f, indent=2)

View File

@@ -58,6 +58,7 @@ class Sensor(threading.Thread):
except queue.Empty: except queue.Empty:
continue continue
class Actuator(threading.Thread): class Actuator(threading.Thread):
def __init__(self, actuator_queue): def __init__(self, actuator_queue):
super().__init__() super().__init__()
@@ -80,6 +81,7 @@ class Actuator(threading.Thread):
except queue.Empty: except queue.Empty:
continue continue
class Cortex(threading.Thread): class Cortex(threading.Thread):
def __init__(self, sensor_queue, neuron_queue, actuator_queue): def __init__(self, sensor_queue, neuron_queue, actuator_queue):
super().__init__() super().__init__()
@@ -102,6 +104,7 @@ class Cortex(threading.Thread):
print("Unknown command. Please use 'sense_think_act' or 'terminate'.") print("Unknown command. Please use 'sense_think_act' or 'terminate'.")
print("Cortex terminated.") print("Cortex terminated.")
if __name__ == "__main__": if __name__ == "__main__":
sensor_queue = queue.Queue() sensor_queue = queue.Queue()
neuron_queue = queue.Queue() neuron_queue = queue.Queue()
@@ -127,4 +130,4 @@ if __name__ == "__main__":
neuron.join() neuron.join()
actuator.join() actuator.join()
print("System terminated.") print("System terminated.")

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
gymnasium==1.2.1
numpy>=1.23
Box2D
pygame