YJack0000 / SimEvo

A Customizable Python Framework written in C++ for Simulating Dynamic Evolutionary Phenomena.
https://pypi.org/project/simevopy/
MIT License
10 stars 0 forks source link

How to make the `interaction` and `reaction` between `EnvironmentObject` be customizable #11

Open YJack0000 opened 3 months ago

YJack0000 commented 3 months ago

Currently the interaction and reaction are hardcoded in Environment, but I think it is important for user to customize the logic behind this two components.

The kind of idea is like

def custom_interaction1(from, target):
    if isInstance(target, Organism):
        print("target is an Organism!!!")
        print(f'{source} is interacting with {target} with custom+interaction1')

def custom_interaction2(from, target):
    if isInstance(target, Food):
        print("target is a Food!!!")
        print(f'{from} is interacting with {target} with custom+interaction2')

env = Environment(1000, 1000)
env.add(Organism(Organism(Genes(dna), interactions=[custom_interaction1, custom_interaction2]))
MarkLai0317 commented 1 month ago

I have come up with a rough design to address this problem. But can you provide the detail of the difference between interaction and reaction before I draw the design diagram?

YJack0000 commented 1 month ago

There are two phase in the handleInteractions

  1. query out the interactable things and interact with it
  2. query out the reactable things and react to it

Both of the stage will call the react or interact function implement of EnvironmentObject, and thos thing are hard coded in Organism and Food.

Screenshot 2024-07-22 at 5 44 46 PM

YJack0000 commented 1 month ago

I seperate interaction and reaction in https://github.com/YJack0000/SimEvo/commit/70fc2c374281ecd97393db6626bfc7c1cf39c414

YJack0000 commented 1 month ago

@MarkLai0317 Can you open a pull request and update the puml first? Maybe I can follow the uml you provided of modify the code

MarkLai0317 commented 1 month ago

Pasted Graphic 1

Above is the design for the interaction and react. I suggest we can confirm the design here.

each EnvironmentObject can have an interact and react interface that can be called by Environment. And EnvironmentObject delegate interact and react to the true implementation of the interaction and reaction to their private method.

This way Environment don't have to know the exact type of the Object if there are new type in the future.

I will create pull request after you approving the design.

YJack0000 commented 1 month ago

Annotation I mention in the DC https://python.langchain.com/v0.1/docs/modules/tools/custom_tools/

MarkLai0317 commented 1 month ago
class BaseInteraction:
    def __init__(self, successor):
        self.successor = successor
    @abstractmethod
    def _is_response(self, obj):
        pass

    @abstractmethod
    def _interact(self, obj):
        pass

    def interact(self, obj):

        if self._is_response(self, obj):
            self.interact(self, obj)

        if self.successor is not None:   
        self.successor.interact(obj)

# user implement
class Interaction1(BaseInteraction):
    def _is_response(self, obj):
        # check type or name
        # 

    def _interact(self, obj):
        # do interact, no need check type or call successor, just interact

class Interaction2(BaseInteraction):
    def _is_response(self, obj):
        # check type or name
        # 

    def _interact(self, obj):
        # do interact, no need check type or call successor, just interact

class EnvironmentObject:
    def __init__(self, InteractionList: List[BaseInteraction]):
        self.interaction = None
        for interaction in InteractionList:
            self.interaction = interaction(self.interaction)

    def interact(self):
        self.interaction.interact() # will call interaction1, interaction2

e = EnvironmentObject([Interaction1, Interaction2])
e.interact()
YJack0000 commented 1 month ago

I have tried to pass a class inherit in Pyhton back to C++ before. The problem is in C++ still calling the base class in the runtime. If your are managed to implement the feature like this will be great.

YJack0000 commented 1 month ago

I check your example code. And the target object should not try to interact with self object becuase it has its own attention radius.

Also, when will the node call the successor?

class BaseInteraction:
    def __init__(self, successor):
        self.successor = successor
    @abstractmethod
    def _is_response(self, obj):
        pass

    @abstractmethod
    def _interact(self, obj):
        pass

    def interact(self, obj):
        if self._is_response(self, obj):
            self.interact(self, obj)

# user implement
class Interaction1(BaseInteraction):
    def _is_response(self, obj):
        # check type or name
        # 

    def _interact(self, obj):
        # do interact, no need check type or call successor, just interact

class Interaction2(BaseInteraction):
    def _is_response(self, obj):
        # check type or name
        # 

    def _interact(self, obj):
        # do interact, no need check type or call successor, just interact

class EnvironmentObject:
    def __init__(self, InteractionList: List[BaseInteraction]):
        self.interaction = None
        for interaction in InteractionList:
            self.interaction = interaction(self.interaction)

    def interact(self):
        self.interaction.interact() # will call interaction1, interaction2

e = EnvironmentObject(interactions=[Interaction1, Interaction2])
e.interact()
YJack0000 commented 1 month ago

Currently the interaction and reaction are hardcoded in Environment, but I think it is important for user to customize the logic behind this two components.

The kind of idea is like

def custom_interaction1(from, target):
    if isInstance(target, Organism):
        print("target is an Organism!!!")
        print(f'{source} is interacting with {target} with custom+interaction1')

def custom_interaction2(from, target):
    if isInstance(target, Food):
        print("target is a Food!!!")
        print(f'{from} is interacting with {target} with custom+interaction2')

env = Environment(1000, 1000)
env.add(Organism(Organism(Genes(dna), interactions=[custom_interaction1, custom_interaction2]))

I update the body from the original issue.