complete overhaul of python script to simulate boolean networks. not fully bug tested!
This commit is contained in:
400
code/main.py
Normal file
400
code/main.py
Normal file
@@ -0,0 +1,400 @@
|
||||
from functools import partial
|
||||
from types import FunctionType
|
||||
from typing import Callable, Optional
|
||||
|
||||
from simple_term_menu import TerminalMenu
|
||||
from simulator import BooleanNetwork
|
||||
|
||||
|
||||
def ShowMenu(options: list[str], title: str = "", highlight_entry: int = 0) -> int:
|
||||
menu = TerminalMenu(
|
||||
menu_entries=options,
|
||||
title=title,
|
||||
multi_select=False,
|
||||
cursor_index=highlight_entry,
|
||||
)
|
||||
|
||||
selectedIndex = menu.show()
|
||||
|
||||
if selectedIndex is None:
|
||||
selectedIndex = len(options) - 1
|
||||
|
||||
return selectedIndex # pyright: ignore[reportReturnType]
|
||||
|
||||
|
||||
def Continue() -> None:
|
||||
ShowMenu(options=[None, "Continue"])
|
||||
|
||||
|
||||
# def MainMenu() -> None:
|
||||
# global currentFunction
|
||||
# options = ["Simulator", "Calculator", "Quit"]
|
||||
# actions: list[Callable[[], None]] = [SimulatorMenu, StopApplication]
|
||||
|
||||
# selectedIndex = ShowMenu(
|
||||
# options, title="Boolean Network Simulator\nby Tom Zuidberg"
|
||||
# )
|
||||
|
||||
# currentFunction = actions[selectedIndex]
|
||||
|
||||
|
||||
def StopApplication() -> None:
|
||||
quit()
|
||||
|
||||
|
||||
def MainMenu() -> None:
|
||||
global currentFunction
|
||||
|
||||
def SelectExampleMenu() -> None:
|
||||
def bn4nodeSync() -> None:
|
||||
bn = (
|
||||
BooleanNetwork(4)
|
||||
.UseSynchronousScheme()
|
||||
.SetFunctions(
|
||||
[
|
||||
lambda x1, x2, x3, x4: not x2,
|
||||
lambda x1, x2, x3, x4: x1,
|
||||
lambda x1, x2, x3, x4: x1 ^ x4,
|
||||
lambda x1, x2, x3, x4: x3,
|
||||
]
|
||||
)
|
||||
)
|
||||
global currentFunction
|
||||
funcStrings = ["not x2", "x1", "x1 ^ x4", "x3"]
|
||||
currentFunction = partial(BooleanNetworkMenu, bn, funcStrings)
|
||||
|
||||
def bn4nodeSeq() -> None:
|
||||
bn = (
|
||||
BooleanNetwork(4)
|
||||
.UseSequentialScheme([1, 2, 3, 4])
|
||||
.SetFunctions(
|
||||
[
|
||||
lambda x1, x2, x3, x4: not x2,
|
||||
lambda x1, x2, x3, x4: x1,
|
||||
lambda x1, x2, x3, x4: x1 ^ x4,
|
||||
lambda x1, x2, x3, x4: x3,
|
||||
]
|
||||
)
|
||||
)
|
||||
global currentFunction
|
||||
funcStrings = ["not x2", "x1", "x1 ^ x4", "x3"]
|
||||
currentFunction = partial(BooleanNetworkMenu, bn, funcStrings)
|
||||
|
||||
def bn3nodeSync() -> None:
|
||||
bn = (
|
||||
BooleanNetwork(3)
|
||||
.UseSynchronousScheme()
|
||||
.SetFunctions(
|
||||
[
|
||||
lambda x1, x2, x3: not x3,
|
||||
lambda x1, x2, x3: not x1,
|
||||
lambda x1, x2, x3: not x2,
|
||||
]
|
||||
)
|
||||
)
|
||||
global currentFunction
|
||||
funcStrings = ["not x3", "not x1", "not x2"]
|
||||
currentFunction = partial(BooleanNetworkMenu, bn, funcStrings)
|
||||
|
||||
def bn3nodeSeq() -> None:
|
||||
bn = (
|
||||
BooleanNetwork(3)
|
||||
.UseSequentialScheme([1, 2, 3])
|
||||
.SetFunctions(
|
||||
[
|
||||
lambda x1, x2, x3: not x3,
|
||||
lambda x1, x2, x3: not x1,
|
||||
lambda x1, x2, x3: not x2,
|
||||
]
|
||||
)
|
||||
)
|
||||
global currentFunction
|
||||
funcStrings = ["not x3", "not x1", "not x2"]
|
||||
currentFunction = partial(BooleanNetworkMenu, bn, funcStrings)
|
||||
|
||||
def ReturnHelper() -> None:
|
||||
global currentFunction
|
||||
currentFunction = MainMenu
|
||||
|
||||
title = "Select an example boolean network:"
|
||||
options = [
|
||||
"4-node network with synchronous update",
|
||||
"4-node network with sequential update",
|
||||
"3-node repressilator network with synchronous update",
|
||||
"3-node repressilator network with sequential update",
|
||||
"Return to main menu",
|
||||
]
|
||||
actions = [bn4nodeSync, bn4nodeSeq, bn3nodeSync, bn3nodeSeq, ReturnHelper]
|
||||
|
||||
actions[ShowMenu(options=options, title=title)]()
|
||||
|
||||
options = [
|
||||
"Set up a new boolean network",
|
||||
"Set up a boolean network from existing ones",
|
||||
"Quit",
|
||||
]
|
||||
|
||||
actions: list[Callable[[], None]] = [
|
||||
BooleanNetworkMenu,
|
||||
SelectExampleMenu,
|
||||
StopApplication,
|
||||
]
|
||||
|
||||
selectedIndex = ShowMenu(
|
||||
options, title="Boolean Network Simulator\nby Tom Zuidberg"
|
||||
)
|
||||
|
||||
currentFunction = actions[selectedIndex]
|
||||
|
||||
|
||||
def BooleanNetworkMenu(
|
||||
bn: Optional[BooleanNetwork] = None, funcStrings: Optional[list[str]] = None
|
||||
) -> None:
|
||||
global currentFunction
|
||||
|
||||
boolNetwork: BooleanNetwork = BooleanNetwork(3)
|
||||
verbose: bool = False
|
||||
done: bool = False
|
||||
functions: list = [None for _ in range(3)]
|
||||
functionStrings: list = [None for _ in range(3)]
|
||||
functionsDirtyFlag = False
|
||||
current_highlight = 0
|
||||
|
||||
if bn is not None and funcStrings is not None:
|
||||
boolNetwork = bn
|
||||
verbose = False
|
||||
functions = [None for _ in range(3)]
|
||||
functionStrings = funcStrings
|
||||
|
||||
def Menu() -> None:
|
||||
nonlocal current_highlight
|
||||
title: str = f"Boolean Network: {boolNetwork} (timeStep | state)"
|
||||
options = [
|
||||
f"Set size (current: {boolNetwork.size}) WARNING: this will reset all other options!",
|
||||
f"Set state (current: {str(boolNetwork)[-boolNetwork.size :]})",
|
||||
f"Set update scheme (current: {boolNetwork.updateScheme})",
|
||||
*[
|
||||
f"Set update function of node x{i} (current: {functionStrings[i - 1]})"
|
||||
for i in range(1, boolNetwork.size + 1)
|
||||
],
|
||||
None,
|
||||
f"Toggle verbose update: (current: {verbose})",
|
||||
"Update once",
|
||||
"Update multiple times",
|
||||
"Get stationairy distribution",
|
||||
None,
|
||||
"Return to main menu",
|
||||
]
|
||||
|
||||
actions = [
|
||||
SetSizeHelper,
|
||||
SetStateHelper,
|
||||
SetUpdateSchemeHelper,
|
||||
*[partial(SetFunctionHelper, i) for i in range(boolNetwork.size)],
|
||||
None,
|
||||
ToggleVerboseHelper,
|
||||
UpdateHelper,
|
||||
MultiUpdateHelper,
|
||||
GetStableDistrHelper,
|
||||
None,
|
||||
ReturnHelper,
|
||||
]
|
||||
|
||||
selectedIndex = ShowMenu(
|
||||
options=options, title=title, highlight_entry=current_highlight
|
||||
)
|
||||
current_highlight = selectedIndex
|
||||
actions[selectedIndex]()
|
||||
|
||||
def SetSizeHelper() -> None:
|
||||
nonlocal boolNetwork, functions, functionStrings
|
||||
while True:
|
||||
size = input("Set new size (Leave empty to cancel):\n").strip()
|
||||
if size == "":
|
||||
print("Cancelled")
|
||||
return
|
||||
try:
|
||||
size = int(size)
|
||||
except ValueError:
|
||||
print("Please enter only integers.")
|
||||
continue
|
||||
|
||||
boolNetwork = BooleanNetwork(size)
|
||||
functions = [None for _ in range(size)]
|
||||
functionStrings = [None for _ in range(size)]
|
||||
return
|
||||
|
||||
def SetStateHelper() -> None:
|
||||
nonlocal boolNetwork
|
||||
while True:
|
||||
state = input(
|
||||
"Set new state (Leave empty to cancel).\nAccepted format example: '01001'\n"
|
||||
).strip()
|
||||
if state == "":
|
||||
print("Cancelled")
|
||||
return
|
||||
try:
|
||||
boolNetwork.SetState(state)
|
||||
return
|
||||
except AssertionError as e:
|
||||
print("Invalid input:", e)
|
||||
|
||||
def SetUpdateSchemeHelper() -> None:
|
||||
nonlocal boolNetwork
|
||||
|
||||
def SetSequentialHelper() -> None:
|
||||
while True:
|
||||
seq = input(
|
||||
"Set sequence. (Leave empty to cancel)\nFormat example: '1,4,3,2'\n"
|
||||
).strip()
|
||||
if seq == "":
|
||||
print("Cancelled")
|
||||
return
|
||||
|
||||
seq = seq.split(",")
|
||||
try:
|
||||
seq = [int(i) for i in seq]
|
||||
boolNetwork.UseSequentialScheme(seq)
|
||||
return
|
||||
except Exception as e:
|
||||
print("Invalid input:", e)
|
||||
|
||||
def SetProbabilisticHelper() -> None:
|
||||
while True:
|
||||
chance = input(
|
||||
"Set flip chance as float between 0.0 and 1.0. (Leave empty to cancel)\n"
|
||||
).strip()
|
||||
if chance == "":
|
||||
print("Cancelled")
|
||||
return
|
||||
|
||||
try:
|
||||
chance = float(chance)
|
||||
boolNetwork.UseProbabilisticScheme(chance)
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print("Invalid input:", e)
|
||||
|
||||
title = "Select update scheme:"
|
||||
options = [
|
||||
"Synchronous update scheme",
|
||||
"Sequential update scheme",
|
||||
"Probabilistic update scheme",
|
||||
"Asynchronous random update scheme",
|
||||
"Cancel",
|
||||
]
|
||||
actions = [
|
||||
lambda: boolNetwork.UseSynchronousScheme(),
|
||||
SetSequentialHelper,
|
||||
SetProbabilisticHelper,
|
||||
lambda: boolNetwork.UseAsynchronousRandomScheme(),
|
||||
lambda: None,
|
||||
]
|
||||
|
||||
actions[ShowMenu(options=options, title=title)]()
|
||||
|
||||
def UpdateHelper() -> None:
|
||||
nonlocal functions, boolNetwork, functionsDirtyFlag
|
||||
if functionsDirtyFlag:
|
||||
try:
|
||||
boolNetwork.SetFunctions(functions)
|
||||
functionsDirtyFlag = False
|
||||
except Exception as e:
|
||||
print("Error while updating the functions:", e)
|
||||
return
|
||||
|
||||
try:
|
||||
boolNetwork.Update()
|
||||
except Exception as e:
|
||||
print("Error while simulating once:", e)
|
||||
|
||||
def MultiUpdateHelper() -> None:
|
||||
nonlocal boolNetwork, verbose, functions, functionsDirtyFlag
|
||||
if functionsDirtyFlag:
|
||||
try:
|
||||
boolNetwork.SetFunctions(functions)
|
||||
functionsDirtyFlag = False
|
||||
except Exception as e:
|
||||
print("Error while updating functions:", e)
|
||||
|
||||
n = 0
|
||||
while True:
|
||||
n = input(
|
||||
"Enter amount of time steps to be simulated. (Leave empty to cancel)\n"
|
||||
).strip()
|
||||
if n == "":
|
||||
print("Cancelled")
|
||||
return
|
||||
|
||||
try:
|
||||
n = int(n)
|
||||
if n < 0:
|
||||
raise ValueError
|
||||
break
|
||||
except ValueError:
|
||||
print("Invalid input")
|
||||
try:
|
||||
boolNetwork.Update(n, verbose=verbose)
|
||||
if verbose:
|
||||
Continue()
|
||||
print("\n")
|
||||
except Exception as e:
|
||||
print("Error while simulating:", e)
|
||||
|
||||
def GetStableDistrHelper() -> None:
|
||||
print(boolNetwork.GetStableProbabilityDistribution())
|
||||
Continue()
|
||||
print("\n")
|
||||
|
||||
def SetFunctionHelper(index: int) -> None:
|
||||
nonlocal boolNetwork, functions, functionStrings, functionsDirtyFlag
|
||||
while True:
|
||||
funcString = input(
|
||||
f"Set new function for node x{index + 1}. The function will receive all nodes in form of x1, x2, ..., x[size]. (Leave empty to cancel)\n"
|
||||
)
|
||||
if funcString == "":
|
||||
print("Cancelled")
|
||||
return
|
||||
try:
|
||||
func = (
|
||||
"lambda "
|
||||
+ ",".join(f"x{i}" for i in range(1, boolNetwork.size + 1))
|
||||
+ ":"
|
||||
+ funcString
|
||||
)
|
||||
func = eval(func)
|
||||
if not isinstance(func, FunctionType):
|
||||
print("Please enter a valid function. Got: " + func)
|
||||
continue
|
||||
functions[index] = func
|
||||
functionStrings[index] = funcString
|
||||
functionsDirtyFlag = True
|
||||
return
|
||||
except Exception as e:
|
||||
print("Error while parsing function:", e)
|
||||
|
||||
def ToggleVerboseHelper():
|
||||
nonlocal verbose
|
||||
verbose = not verbose
|
||||
|
||||
def ReturnHelper():
|
||||
nonlocal done
|
||||
done = True
|
||||
|
||||
while not done:
|
||||
Menu()
|
||||
|
||||
currentFunction = MainMenu
|
||||
|
||||
|
||||
def main() -> None:
|
||||
global currentFunction
|
||||
currentFunction = MainMenu
|
||||
|
||||
while True:
|
||||
currentFunction()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user