diff --git a/polymorphic_solver/.DS_Store b/polymorphic_solver/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b739869f42a1207ee2c5d2e5dcb4f86d76c22dce Binary files /dev/null and b/polymorphic_solver/.DS_Store differ diff --git a/polymorphic_solver/Dockerfile b/polymorphic_solver/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3e59f4028860da93ecb40c584a8077d43659d1c8 --- /dev/null +++ b/polymorphic_solver/Dockerfile @@ -0,0 +1,28 @@ +FROM python:3.5 +RUN pip install --upgrade pip +COPY ./requirements.txt / +RUN pip install -r /requirements.txt +RUN apt-get update +RUN apt-get install -y git supervisor +RUN git clone https://github.com/openai/multiagent-particle-envs +RUN pip install -e ./multiagent-particle-envs +RUN mkdir /app +COPY ./src /app/ +#RUN git clone https://www.github.com/alexandrosraikos/dependency-extractor +#RUN pip install ./dependency-extractor +WORKDIR /app + +EXPOSE 7879 +ENV GIT_PYTHON_REFRESH=quiet + +# Execute both in entrypoint.sh. +#ENTRYPOINT ["/entrypoint.sh"] +RUN mkdir -p /run/pid +RUN mkdir -p /var/log/supervisor + +RUN virtualenv -p python3.7 /apivenv +RUN /apivenv/bin/pip install fastapi uvicorn pydantic stomp.py slugify + +#CMD ["/profiler/start.sh"] +COPY ./src/supervisord.conf /etc/supervisor/conf.d/supervisord.conf +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] \ No newline at end of file diff --git a/polymorphic_solver/docker-compose.yaml b/polymorphic_solver/docker-compose.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ce461fc5135be09c2a4ad4d984d971f7392c5f79 --- /dev/null +++ b/polymorphic_solver/docker-compose.yaml @@ -0,0 +1,52 @@ +version: '2' + +services: + activemq: + image: jdtotow/activemq + container_name: activemq + ports: + # mqtt + - "1883:1883" + # amqp + - "5672:5672" + # ui + - "8161:8161" + # stomp + - "61613:61613" + # ws + - "61614:61614" + # jms + - "61616:61616" + # jms prometheus agent + - "8080:8080" + #volumes: ["activemq-data:/opt/activemq/conf", "activemq-data:/data/activemq", "activemq-data:/var/log/activemq"] + environment: + ACTIVEMQ_REMOVE_DEFAULT_ACCOUNT: "true" + ACTIVEMQ_ADMIN_LOGIN: aaa + ACTIVEMQ_ADMIN_PASSWORD: "111" + ACTIVEMQ_WRITE_LOGIN: aaa + ACTIVEMQ_WRITE_PASSWORD: "111" + ACTIVEMQ_READ_LOGIN: aaa + ACTIVEMQ_READ_PASSWORD: "111" + ACTIVEMQ_JMX_LOGIN: aaa + ACTIVEMQ_JMX_PASSWORD: "111" + ACTIVEMQ_STATIC_TOPICS: static-topic-1;static-topic-2 + ACTIVEMQ_STATIC_QUEUES: static-queue-1;static-queue-2 + ACTIVEMQ_ENABLED_SCHEDULER: "true" + ACTIVEMQ_MIN_MEMORY: 512 + ACTIVEMQ_MAX_MEMORY: 2048 + + solver: + image: polymorphic_solver + build: + context: . + container_name: solver + restart: always + ports: + - 7879:7879 + environment: + - "ACTIVEMQ_HOST=activemq" + volumes: + - "/tmp/solver/json:/json" + + diff --git a/polymorphic_solver/requirements.txt b/polymorphic_solver/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..6061a471e32040052544b6f4ff4edc42e2fc3787 --- /dev/null +++ b/polymorphic_solver/requirements.txt @@ -0,0 +1,7 @@ +gym==0.10.5 +numpy==1.14.5 +torch +requests +stomp.py +slugify +virtualenv \ No newline at end of file diff --git a/polymorphic_solver/src/.DS_Store b/polymorphic_solver/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6064c3c6b3a788b403e56e1826d724b5e55f503e Binary files /dev/null and b/polymorphic_solver/src/.DS_Store differ diff --git a/polymorphic_solver/src/1.4.0 b/polymorphic_solver/src/1.4.0 new file mode 100644 index 0000000000000000000000000000000000000000..bbc0f6b5d37bf92c7eef1c07b850bcb16676cf88 --- /dev/null +++ b/polymorphic_solver/src/1.4.0 @@ -0,0 +1,4 @@ +Collecting torch + Downloading torch-1.10.1-cp39-none-macosx_10_9_x86_64.whl (147.1 MB) +Requirement already satisfied: typing-extensions in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from torch) (3.7.4.3) +Installing collected packages: torch diff --git a/polymorphic_solver/src/__pycache__/agent.cpython-35.pyc b/polymorphic_solver/src/__pycache__/agent.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c9b1a4b8e26879c9847db2e36fb20edf059c5a4 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/agent.cpython-35.pyc differ diff --git a/polymorphic_solver/src/__pycache__/agent.cpython-36.pyc b/polymorphic_solver/src/__pycache__/agent.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba97608d3fbb4e65ef3570adf4812e06784ed4a4 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/agent.cpython-36.pyc differ diff --git a/polymorphic_solver/src/__pycache__/agent.cpython-39.pyc b/polymorphic_solver/src/__pycache__/agent.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9931b953374710ea5a072384d33ee10ed18c64fb Binary files /dev/null and b/polymorphic_solver/src/__pycache__/agent.cpython-39.pyc differ diff --git a/polymorphic_solver/src/__pycache__/buffer.cpython-35.pyc b/polymorphic_solver/src/__pycache__/buffer.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9042a6f216502223302f6b860ebc2930cda0c20d Binary files /dev/null and b/polymorphic_solver/src/__pycache__/buffer.cpython-35.pyc differ diff --git a/polymorphic_solver/src/__pycache__/buffer.cpython-36.pyc b/polymorphic_solver/src/__pycache__/buffer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..248879af3c295d5181bf354d13019ee8aa637873 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/buffer.cpython-36.pyc differ diff --git a/polymorphic_solver/src/__pycache__/buffer.cpython-39.pyc b/polymorphic_solver/src/__pycache__/buffer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..590f278249783c9ddcfac1998f99cdf99d7668fb Binary files /dev/null and b/polymorphic_solver/src/__pycache__/buffer.cpython-39.pyc differ diff --git a/polymorphic_solver/src/__pycache__/env.cpython-35.pyc b/polymorphic_solver/src/__pycache__/env.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9bd50c515190eb902731c40139ceb4714869a94 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/env.cpython-35.pyc differ diff --git a/polymorphic_solver/src/__pycache__/env.cpython-36.pyc b/polymorphic_solver/src/__pycache__/env.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..608ea5761c240ef052b0764bc36358fc088b1937 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/env.cpython-36.pyc differ diff --git a/polymorphic_solver/src/__pycache__/env.cpython-39.pyc b/polymorphic_solver/src/__pycache__/env.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eee830cad1099f9aba799e542f655702a35c1fe3 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/env.cpython-39.pyc differ diff --git a/polymorphic_solver/src/__pycache__/maddpg.cpython-35.pyc b/polymorphic_solver/src/__pycache__/maddpg.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f9207d8ef5a5a73457c2caeac6d1405bb534fe0 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/maddpg.cpython-35.pyc differ diff --git a/polymorphic_solver/src/__pycache__/maddpg.cpython-36.pyc b/polymorphic_solver/src/__pycache__/maddpg.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3e8d6e54bc94f2171a5c9493511a6f8a0628547 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/maddpg.cpython-36.pyc differ diff --git a/polymorphic_solver/src/__pycache__/maddpg.cpython-39.pyc b/polymorphic_solver/src/__pycache__/maddpg.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03ab79db76b04a82bec681c2d882284158d7819b Binary files /dev/null and b/polymorphic_solver/src/__pycache__/maddpg.cpython-39.pyc differ diff --git a/polymorphic_solver/src/__pycache__/make_env.cpython-35.pyc b/polymorphic_solver/src/__pycache__/make_env.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f2d8516b1eed8300e6d0a9366b2e766d51b2d77 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/make_env.cpython-35.pyc differ diff --git a/polymorphic_solver/src/__pycache__/make_env.cpython-36.pyc b/polymorphic_solver/src/__pycache__/make_env.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de9537cf64c3e73e1059985953b720bfcf1658a2 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/make_env.cpython-36.pyc differ diff --git a/polymorphic_solver/src/__pycache__/make_env.cpython-39.pyc b/polymorphic_solver/src/__pycache__/make_env.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82ab135256300ca4b1a17a23d40e861ff4d57774 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/make_env.cpython-39.pyc differ diff --git a/polymorphic_solver/src/__pycache__/morphemic.cpython-35.pyc b/polymorphic_solver/src/__pycache__/morphemic.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b0125e0dc07f4acf748bcff0506ddae7cc030a3 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/morphemic.cpython-35.pyc differ diff --git a/polymorphic_solver/src/__pycache__/networks.cpython-35.pyc b/polymorphic_solver/src/__pycache__/networks.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1c0e15c03f63331ce063dbdc1d8c8d970d25156 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/networks.cpython-35.pyc differ diff --git a/polymorphic_solver/src/__pycache__/networks.cpython-36.pyc b/polymorphic_solver/src/__pycache__/networks.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4b2297cb70bf5aef4f0f0d8de36ef434450f736 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/networks.cpython-36.pyc differ diff --git a/polymorphic_solver/src/__pycache__/networks.cpython-39.pyc b/polymorphic_solver/src/__pycache__/networks.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f13c01103ac4ca896e1a5a3cb4b2de8a9ad52db Binary files /dev/null and b/polymorphic_solver/src/__pycache__/networks.cpython-39.pyc differ diff --git a/polymorphic_solver/src/__pycache__/resource_logic.cpython-35.pyc b/polymorphic_solver/src/__pycache__/resource_logic.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfb5c9ad0999b772b5a50e0d8f0a550a994e4a76 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/resource_logic.cpython-35.pyc differ diff --git a/polymorphic_solver/src/__pycache__/resource_logic.cpython-36.pyc b/polymorphic_solver/src/__pycache__/resource_logic.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..651ea31d80f4235d086c790661ba65943e344268 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/resource_logic.cpython-36.pyc differ diff --git a/polymorphic_solver/src/__pycache__/resource_logic.cpython-39.pyc b/polymorphic_solver/src/__pycache__/resource_logic.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93fb2e2e956d995789616afcbfe4dccadfebfd67 Binary files /dev/null and b/polymorphic_solver/src/__pycache__/resource_logic.cpython-39.pyc differ diff --git a/polymorphic_solver/src/agent.py b/polymorphic_solver/src/agent.py new file mode 100644 index 0000000000000000000000000000000000000000..1cf055d2c92112790a33befc36aba10475a1f2cf --- /dev/null +++ b/polymorphic_solver/src/agent.py @@ -0,0 +1,74 @@ +import torch as T +from networks import ActorNetwork, CriticNetwork +from os import path + +class Agent: + def __init__(self, actor_dims, critic_dims, n_actions, n_agents, agent_idx, chkpt_dir, + alpha=0.01, beta=0.01, fc1=64, + fc2=64, gamma=0.95, tau=0.01): + self.gamma = gamma + self.tau = tau + self.n_actions = n_actions + self.chkpt_file = chkpt_dir + self.agent_name = 'agent_%s' % agent_idx + self.actor = ActorNetwork(alpha, actor_dims, fc1, fc2, n_actions, + chkpt_dir=chkpt_dir, name=self.agent_name+'_actor') + self.critic = CriticNetwork(beta, critic_dims, + fc1, fc2, n_agents, n_actions, + chkpt_dir=chkpt_dir, name=self.agent_name+'_critic') + self.target_actor = ActorNetwork(alpha, actor_dims, fc1, fc2, n_actions, + chkpt_dir=chkpt_dir, + name=self.agent_name+'_target_actor') + self.target_critic = CriticNetwork(beta, critic_dims, + fc1, fc2, n_agents, n_actions, + chkpt_dir=chkpt_dir, + name=self.agent_name+'_target_critic') + + self.update_network_parameters(tau=1) + + def choose_action(self, observation): + state = T.tensor([observation], dtype=T.float).to(self.actor.device) + actions = self.actor.forward(state) + noise = T.rand(self.n_actions).to(self.actor.device) + action = actions + noise + + return action.detach().cpu().numpy()[0] + + def update_network_parameters(self, tau=None): + if tau is None: + tau = self.tau + + target_actor_params = self.target_actor.named_parameters() + actor_params = self.actor.named_parameters() + + target_actor_state_dict = dict(target_actor_params) + actor_state_dict = dict(actor_params) + for name in actor_state_dict: + actor_state_dict[name] = tau*actor_state_dict[name].clone() + \ + (1-tau)*target_actor_state_dict[name].clone() + + self.target_actor.load_state_dict(actor_state_dict) + + target_critic_params = self.target_critic.named_parameters() + critic_params = self.critic.named_parameters() + + target_critic_state_dict = dict(target_critic_params) + critic_state_dict = dict(critic_params) + for name in critic_state_dict: + critic_state_dict[name] = tau*critic_state_dict[name].clone() + \ + (1-tau)*target_critic_state_dict[name].clone() + + self.target_critic.load_state_dict(critic_state_dict) + + def save_models(self): + self.actor.save_checkpoint() + self.target_actor.save_checkpoint() + self.critic.save_checkpoint() + self.target_critic.save_checkpoint() + + def load_models(self): + if path.exists(self.chkpt_file+"/agent_0_critic"): + self.actor.load_checkpoint() + self.target_actor.load_checkpoint() + self.critic.load_checkpoint() + self.target_critic.load_checkpoint() diff --git a/polymorphic_solver/src/amq_client/Event.py b/polymorphic_solver/src/amq_client/Event.py new file mode 100644 index 0000000000000000000000000000000000000000..52dbc842ef3c729386bed8f8e7cd4b92bc9420c6 --- /dev/null +++ b/polymorphic_solver/src/amq_client/Event.py @@ -0,0 +1,420 @@ + + +class Metric(enumerate): + """ + [0] (current/detected) Metrics & SLOs Events Format: + + + This event is aggregated by EMS and it is persisted in InfluxDB. Moreover, + Prediction Orchestrator will subscribe and receive the current metrics in order to + evaluate the forecasting methods, according to the defined KPIs (e.g., MAPE) + + * Topic: [metric_name] + > (e.g. MaxCPULoad) + + + { + "metricValue": 12.34, + + "level": 1, + + "timestamp": 143532341251, + + "refersTo": "MySQL_12345", + + "cloud": "AWS-Dublin", + + "provider": "AWS" + + } + + + + https://confluence.7bulls.eu/display/MOR/Forecasting+Mechanism+Sub-components+Communication + + """ + TIMESTAMP = "timestamp" + METRIC_VALUE = "metricValue" + REFERS_TO = "refersTo" + CLOUD = "cloud" + PROVIDER = "provider" + + + +class PredictionMetric(enumerate): + + """ + [1] Predicted Metrics & SLOs Events Format + + + This event is produced by the Prediction Orchestrator and reflects the final predicted value for a metric. + + - Topic: prediction.[metric_name] + > (e.g. prediction.MaxCPULoad) + + + { + "metricValue": 12.34, + + "level": 1, + + "timestamp": 143532341251, + + "probability": 0.98, + + "confidence_interval " : [8,15] + + "predictionTime": 143532342, + + "refersTo": "MySQL_12345", + + "cloud": "AWS-Dublin", + + "provider": "AWS" + + } + + + https://confluence.7bulls.eu/display/MOR/Forecasting+Mechanism+Sub-components+Communication + + """ + + _match = "prediction." + + METRICVALUE= "metricValue" + '''Predicted metric value''' + LEVEL= "level" + '''Level of VM where prediction occurred or refers''' + TIMESTAMP= "timestamp" + '''Prediction creation date/time from epoch''' + PROBABILITY= "probability" + '''Probability of the predicted metric value (range 0..1)''' + CONFIDENCE_INTERVAL= "confidence_interval" + '''the probability-confidence interval for the prediction''' + PREDICTION_TIME= "predictionTime" + '''This refers to time point in the imminent future (that is relative to the time + that is needed for reconfiguration) for which the predicted value is considered + valid/accurate (in UNIX Epoch)''' + REFERSTO= "refersTo" + '''The id of the application or component or (VM) host for which the prediction refers to''' + CLOUD= "cloud" + '''Cloud provider of the VM (with location)''' + PROVIDER= "provider" + '''Cloud provider name''' + + + +class MetricsToPredict(enumerate): + + """ + [2] Translator – to – Forecasting Methods/Prediction Orchestrator Events Format + + + This event is produced by the translator, to: + + imform Dataset Maker which metrics should subscribe to in order to aggregate the appropriate tanning dataset in the time-series DB. + instruct each of the Forecasting methods to predict the values of one or more monitoring metrics + inform the Prediction Orchestrator for the metrics which will be forecasted + + * Topic: metrics_to_predict + + + *Note:* This event could be communicated through Mule + + + [ + { + + "metric": "MaxCPULoad", + + "level": 3, + + "publish_rate": 60000, + + }, + + { + + "metric": "MinCPULoad", + + "level": 3, + + "publish_rate": 50000, + + } + + ] + + + https://confluence.7bulls.eu/display/MOR/Forecasting+Mechanism+Sub-components+Communication + + """ + + _match = "metrics_to_predict" + + METRIC = "metric" + '''name of the metric to predict''' + LEVEL = "level" + '''Level of monitoring topology where this metric may be produced/found''' + PUBLISH_RATE = "publish_rate" + '''expected rate for datapoints regarding the specific metric (according to CAMEL)''' + + +class TraningModels(enumerate): + """ + + [3] Forecasting Methods – to – Prediction Orchestrator Events Format + + + This event is produced by each of the Forecasting methods, to inform the + Prediction Orchestrator that the method has (re-)trained its model for one or more metrics. + + * Topic: training_models + + + { + + "metrics": ["MaxCPULoad","MinCPULoad"]", + + "forecasting_method": "ESHybrid", + + "timestamp": 143532341251, + + } + + + https://confluence.7bulls.eu/display/MOR/Forecasting+Mechanism+Sub-components+Communication + + """ + _match = "training_models" + + METRICS = "metrics" + '''metrics for which a certain forecasting method has successfully trained or re-trained its model''' + FORECASTING_METHOD = "forecasting_method" + '''the method that is currently re-training its models''' + TIMESTAMP = "timestamp" + '''date/time of model(s) (re-)training''' + + +class IntermediatePrediction(enumerate): + """ + + [4] Forecasting Methods – to – Prediction Orchestrator Events Format + + + This event is produced by each of the Forecasting methods, and is used by the Prediction Orchestrator to determine the final prediction value for the particular metric. + + + * Topic: intermediate_prediction.[forecasting_method].[metric_name] + * (e.g. intermediate_prediction.ESHybrid.MaxCPULoad) + * We note that any component will be able to subscribe to topics like: + * intermediate_prediction.*.MaxCPULoad → gets MaxCPULoad predictions produced by all forecasting methods or + * intermediate_prediction.ESHybrid.* → gets all metrics predictions from ESHybrid method + * We consider that each forecasting method publishes a static (but configurable) number m of predicted values (under the same timestamp) for time points into the future. These time points into the future are relevant to the reconfiguration time that it is needed (and can also be updated). + * For example if we configure m=5 predictions into the future and the reconfiguration time needed is TR=10 minutes, then at t0 a forecasting method publishes 5 events with the same timestamp and prediction times t0+10, t0+20, t0+30, t0+40, t0+50. + + + + { + "metricValue": 12.34, + + "level": 3, + + "timestamp": 143532341251, + + "probability": 0.98, + + "confidence_interval " : [8,15] + + "predictionTime": 143532342, + + "refersTo": "MySQL_12345", + + "cloud": "AWS-Dublin", + + "provider": "AWS" + + } + + + https://confluence.7bulls.eu/display/MOR/Forecasting+Mechanism+Sub-components+Communication + + """ + + _match="intermediate_prediction." + + METRICVALUE = "metricValue" + '''Predicted metric value (more than one such events will be produced for different time points into the future – this can be valuable to the Prediction Orchestrator in certain situations e.g., forecasting method is unreachable for a time period)''' + + LEVEL = "level" + '''Level of VM where prediction occurred or refers''' + + TIMESTAMP = "timestamp" + '''Prediction creation date/time from epoch''' + + PROBABILITY = "probability" + '''Probability of the predicted metric value (range 0..1)''' + + CONFIDENCE_INTERVAL = "confidence_interval" + '''the probability-confidence interval for the prediction''' + + PREDICTION_TIME = "predictionTime" + '''This refers to time point in the imminent future (that is relative to the time that is needed for reconfiguration) for which the predicted value is considered valid/accurate (in UNIX Epoch)''' + + REFERS_TO = "refersTo" + '''The id of the application or component or (VM) host for which the prediction refers to''' + + CLOUD = "cloud" + '''Cloud provider of the VM (with location)''' + + PROVIDER = "provider" + '''Cloud provider name''' + + + +class Prediction(enumerate): + """ + + [5] Prediction Orchestrator – to – Severity-based SLO Violation Detector Events Format + + + This event is used by the Prediction Orchestrator to inform the SLO Violation Detector about the current values of a metric, which can possibly lead to an SLO Violation detection. + + * Topic: prediction.[metric_name] + * (e.g. prediction.MaxCPULoad) + + + { + "metricValue": 12.34, + + "level": 1, + + "timestamp": 143532341251, + + "probability": 0.98, + + "confidence_interval " : [8,15] + + "predictionTime": 143532342, + + "refersTo": "MySQL_12345", + + "cloud": "AWS-Dublin", + + "provider": "AWS" + + } + + + + https://confluence.7bulls.eu/display/MOR/Forecasting+Mechanism+Sub-components+Communication + + + """ + + _match = "prediction." + + METRICVALUE = "metricValue" + '''Predicted metric value''' + + LEVEL = "level" + '''Level of VM where prediction occurred or refers''' + + TIMESTAMP = "timestamp" + '''Prediction creation date/time from epoch''' + + PROBABILITY = "probability" + '''Probability of the predicted metric value (range 0..1)''' + + CONFIDENCE_INTERVAL = "confidence_interval" + '''the probability-confidence interval for the prediction''' + + PREDICTIONTIME = "predictionTime" + '''This refers to time point in the imminent future (that is relative to the time that is needed for reconfiguration) for which the predicted value is considered valid/accurate (in UNIX Epoch)''' + + REFERSTO = "refersTo" + '''The id of the application or component or (VM) host for which the prediction refers to''' + + CLOUD = "cloud" + '''Cloud provider of the VM (with location)''' + + PROVIDER = "provider" + '''Cloud provider name''' + + +class StopForecasting(enumerate): + """ + [6] Prediction Orchestrator – to – Forecasting Methods Events Format + + + This event is used by the Prediction Orchestrator to instruct a forecasting method to stop producing predicted values for a selection of metrics. + + + * Topic: stop_forecasting.[forecasting_method] + * Each component that implements a specific forecasting method it should subscribe to its relevant topic (e.g. the ES-Hybrid component should subscribe to stop_forecasting.eshybrid topic) + + + { + "metrics": ["MaxCPULoad","MinCPULoad"], + "timestamp": 143532341251, + } + + https://confluence.7bulls.eu/display/MOR/Forecasting+Mechanism+Sub-components+Communication + + + """ + + _match="stop_forecasting." + + METRICS = "metrics" + '''metrics for which a certain method should stop producing predictions (because of poor results)''' + TIMESTAMP = "timestamp" + '''date/time of the command of the orchestrator''' + + +class StartForecasting(enumerate): + """ + + [7] Prediction Orchestrator – to – Forecasting Methods Events Format + + This event is used by the Prediction Orchestrator to instruct a forecasting method to start producing predicted values for a selection of metrics. + + + * Topic: start_forecasting.[forecasting_method] + * Each component that implements a specific forecasting method it should subscribe to its relevant topic (e.g. the ES-Hybrid component should subscribe to start_forecasting.eshybrid topic) + * We consider that each forecasting method should publish a static (but configurable) number m of predicted values (under the same timestamp) for time points into the future. These time points into the future are relevant to the reconfiguration time that it is needed (and can also be updated). + * For example if we configure m=5 predictions into the future and the reconfiguration time needed is TR=10 minutes, then at t0 a forecasting method publishes 5 events with the same timestamp and prediction times t0+10, t0+20, t0+30, t0+40, t0+50. + + + + + { + "metrics": ["MaxCPULoad","MinCPULoad"], + + "timestamp": 143532341251, + + "epoch_start": 143532341252, + + "number_of_forward_predictions": 5, + + "prediction_horizon": 600 + + } + + https://confluence.7bulls.eu/display/MOR/Forecasting+Mechanism+Sub-components+Communication + + + """ + + _match="start_forecasting." + + METRICS = "metrics" + '''metrics for which a certain method should start producing predictions''' + TIMESTAMP = "timestamp" + '''date/time of the command of the orchestrator''' + EPOCH_START = "epoch_start" + '''this time refers to the start time after which all predictions will be considered (i.e. t0)''' + NUMBER_OF_FORWARD_PREDICTIONS = "number_of_forward_predictions" + ''' this is a number that indicates how many time points into the future do we need predictions for.''' + PREDICTION_HORIZON = "prediction_horizon" + '''This time equals to the time (in seconds) that is needed for the platform to implement an application reconfiguration (i.e. TR).''' \ No newline at end of file diff --git a/polymorphic_solver/src/amq_client/MorphemicConnection.py b/polymorphic_solver/src/amq_client/MorphemicConnection.py new file mode 100644 index 0000000000000000000000000000000000000000..9fd61b18c35ed6f26bd3197c53d5209aaa77ef41 --- /dev/null +++ b/polymorphic_solver/src/amq_client/MorphemicConnection.py @@ -0,0 +1,71 @@ +import stomp +import logging +import json + +from stomp.listener import PrintingListener + +class Connection: + + subscriptions = [] + + def __init__(self, username, password, + host='localhost', + port=61613, + debug=False): + self.username = username + self.password = password + self.hosts = [(host, port)] + self.conn = stomp.Connection(host_and_ports=self.hosts, auto_content_length=False) + + if debug: + logging.debug("Enabling debug") + self.conn.set_listener('print', PrintingListener()) + + def _build_id(self,topic,id): + return "id.%s-%s" % (topic,id) + + def set_listener(self, id, listener): + if self.conn: + self.conn.set_listener(id,listener) + + def subscribe(self,destination, id, ack='auto'): + if not self.conn: + raise RuntimeError('You need to connect first') + + self.conn.subscribe(destination, id, ack) + + def topic(self,destination, id, ack='auto'): + self.subscribe("/topic/%s" % destination ,self._build_id(destination,id),ack) + + def queue(self,destination, id, ack='auto'): + self.subscribe("/queue/%s" % destination ,self._build_id(destination,id),ack) + + def unsubscribe(self, topic,id): + + if not self.conn: + return + self.conn.unsubscribe(self._build_id(topic,id)) + + + def connect(self, wait=True): + + if not self.conn: + return + + self.conn.connect(self.username, self.password, wait=wait) + + def disconnect(self): + self.conn.disconnect() + + def send_to_topic(self,destination, body, headers={}, **kwargs): + + if not self.conn: + logging.error("Connect first") + return + + str = json.dumps(body) + + self.conn.send(destination="/topic/%s" % destination, + body= str, + content_type="application/json", + headers=headers, **kwargs) diff --git a/polymorphic_solver/src/amq_client/MorphemicListener.py b/polymorphic_solver/src/amq_client/MorphemicListener.py new file mode 100644 index 0000000000000000000000000000000000000000..35e2eeeb41cf864f2ca4d47dded4ee75e3c51721 --- /dev/null +++ b/polymorphic_solver/src/amq_client/MorphemicListener.py @@ -0,0 +1,48 @@ +from json import JSONDecodeError + +from stomp.listener import ConnectionListener +import logging +import json +from slugify import slugify + +class MorphemicListener(ConnectionListener): + def is_topic(self,headers, event): + if not hasattr(event,"_match"): + return False + match = getattr(event,'_match') + return headers.get('destination').startswith(match) + + + def has_topic_name(self,headers, string): + return headers.get('destination').startswith(string) + + def get_topic_name(self,headers): + return headers.get('destination').replace('/topic/','') + + + def has_topic_name(self,headers, string): + return headers.get('destination').startswith(string) + + def get_topic_name(self,headers): + return headers.get('destination').replace('/topic/','') + + + def on(self,headers, res): + logging.debug("Unknown message %s %s ",headers, res) + pass + + def on_message(self, headers, body): + + logging.debug("Headers %s",headers) + logging.debug(" %s",body) + + try: + res = json.loads(body) + func_name='on_%s' % slugify(headers.get('destination').replace('/topic/',''), separator='_',) + if hasattr(self,func_name): + func = getattr(self, func_name) + func(res) + else: + self.on(headers,res) + except JSONDecodeError: + logging.error("Error decoding %s", body) \ No newline at end of file diff --git a/polymorphic_solver/src/amq_client/Payloads.py b/polymorphic_solver/src/amq_client/Payloads.py new file mode 100644 index 0000000000000000000000000000000000000000..5de1adc844e289db185d74f7c7d76bca0045d686 --- /dev/null +++ b/polymorphic_solver/src/amq_client/Payloads.py @@ -0,0 +1,10 @@ + +class MetricsToPredict: + + + def load(self,body): + self.metrics = body["metrics"] + self.timestamp = body["timestamp"] + self.epoch_start = body["epoch_start"] + self.number_of_forward_predictions = body["number_of_forward_predictions"] + self.prediction_horizon = body["prediction_horizon"] diff --git a/polymorphic_solver/src/amq_client/__init__.py b/polymorphic_solver/src/amq_client/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..45fe25b1772cc225e9ec364aa83620bd07f4e3a7 --- /dev/null +++ b/polymorphic_solver/src/amq_client/__init__.py @@ -0,0 +1,5 @@ + +from . import MorphemicConnection as morphemic +from . import MorphemicListener as listener +from . import Event as events +from . import Payloads as payloads \ No newline at end of file diff --git a/polymorphic_solver/src/amq_client/__pycache__/Event.cpython-36.pyc b/polymorphic_solver/src/amq_client/__pycache__/Event.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..506d8457fd4e3d365eeed89da1e927ff6e9709a0 Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/Event.cpython-36.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/Event.cpython-39.pyc b/polymorphic_solver/src/amq_client/__pycache__/Event.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac2f7d8d3c1b930b7264dab546978a9fc511e7e5 Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/Event.cpython-39.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/MorphemicConnection.cpython-36.pyc b/polymorphic_solver/src/amq_client/__pycache__/MorphemicConnection.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b06e3807a31297d0c3bfca458bd3b5c586b5c10 Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/MorphemicConnection.cpython-36.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/MorphemicConnection.cpython-39.pyc b/polymorphic_solver/src/amq_client/__pycache__/MorphemicConnection.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23c59981651c2e03d96a9175a2ea97f0fecafd90 Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/MorphemicConnection.cpython-39.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/MorphemicListener.cpython-36.pyc b/polymorphic_solver/src/amq_client/__pycache__/MorphemicListener.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6a0c4f95cec6936c9deb635bb19477f7bd3e9be Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/MorphemicListener.cpython-36.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/MorphemicListener.cpython-39.pyc b/polymorphic_solver/src/amq_client/__pycache__/MorphemicListener.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df3a0a2232f4261c00ccc79e17148648016bb2c5 Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/MorphemicListener.cpython-39.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/Payloads.cpython-36.pyc b/polymorphic_solver/src/amq_client/__pycache__/Payloads.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f1a799bde9d88212253b4d049e50f8f68799e6f Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/Payloads.cpython-36.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/Payloads.cpython-39.pyc b/polymorphic_solver/src/amq_client/__pycache__/Payloads.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1ba50f760da2c94569e90e538d5a606627bd59a Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/Payloads.cpython-39.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/__init__.cpython-36.pyc b/polymorphic_solver/src/amq_client/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a317efdfae353ad4c2716a589dfdd263b7df7e83 Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/__init__.cpython-36.pyc differ diff --git a/polymorphic_solver/src/amq_client/__pycache__/__init__.cpython-39.pyc b/polymorphic_solver/src/amq_client/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..714d41dc320f4b41c9afbf451f611548fb7e1940 Binary files /dev/null and b/polymorphic_solver/src/amq_client/__pycache__/__init__.cpython-39.pyc differ diff --git a/polymorphic_solver/src/api.py b/polymorphic_solver/src/api.py new file mode 100644 index 0000000000000000000000000000000000000000..4ea7929e725313a55da3114c576f38d397621703 --- /dev/null +++ b/polymorphic_solver/src/api.py @@ -0,0 +1,195 @@ +import os, time, json, logging, uvicorn +from fastapi import FastAPI +from pydantic import BaseModel +from typing import List +from threading import Thread +from amq_client.MorphemicConnection import Connection + +activemq_hostname = os.environ.get("ACTIVEMQ_HOST", "localhost") +activemq_port = int(os.environ.get("ACTIVEMQ_PORT", "61613")) +activemq_topic = os.environ.get("ACTIVEMQ_TOPIC", "static-topic-1") +activemq_subs_key = os.environ.get("ACTIVEMQ_SUBS_KEY", "subs-1") +activemq_username = os.environ.get("ACTIVEMQ_USERNAME", "aaa") +activemq_password = os.environ.get("ACTIVEMQ_PASSWORD", "111") + +polymorphic_solver_topic = os.environ.get("PROFILER_TOPIC","polymorphic_solver") +json_shared_folder = os.environ.get("JSON_SHARED_FOLDER","./json") +list_variants = os.environ.get("LIST_VARIANTS","VM,DOCKER,SERVERLESS,EDGE,HPC").split(",") +list_hws = os.environ.get("LIST_HWS","CPU,GPU,FPGA").split(",") + +logname = "./log/solver.log" +logging.basicConfig(filename=logname,filemode='a',format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',datefmt='%H:%M:%S',level=logging.DEBUG) +#{"utility": "value","json_path": "path_to_the_json", "result":[["Component_1","Component_2"],["Component_3"]]} + +class Publisher(Thread): + def __init__(self): + self.message = None + self.destination = None + self.client = None + super(Publisher, self).__init__() + + def setParameters(self, message, queue): + self.message = message + self.queue = queue + + def run(self): + self.connect() + while True: + time.sleep(2) + + def connect(self): + while True: + try: + print('The publisher tries to connect to ActiveMQ broker') + logging.info('The publisher tries to connect to ActiveMQ broker') + self.client = Connection(username=activemq_username, password=activemq_password, host=activemq_hostname,port=activemq_port, debug=False) + self.client.connect() + print("connection established") + logging.info("connection established") + return True + except: + print("Could not connect the publisher") + logging.error("Could not connect the publisher") + + def send(self): + if self.message == None or self.queue == None: + print("Message or queue is None") + return False + try: + #self.client.send(body=json.dumps(self.message), destination=self.queue, persistent='false', auto_content_length=False, content_type="application/json") + self.client.send_to_topic(self.queue, self.message) + return True + except Exception as e: + print(e) + self.client.disconnect() + print("Reconnection in 10s ...") + logging.info("Reconnection in 10s ...") + time.sleep(10) + self.connect() + self.send() + +class ApplicationDataRequest(BaseModel): + utility: str + json_path: str + result: List[list] + +class OptimizationRequest(BaseModel): + utility: str + +app = FastAPI() +publisher = Publisher() +publisher.connect() + +""" +data = { + "application": "test_application", + "components":[ + { + "name": "web_service", + "requirements":[ + {"min_mem": 2, "max_mem": 16, "min_cpu": 1, "max_cpu":16, "max_instances":10} #[2,4,8,16] + ], + "variants": ["DOCKER","VM"], + "hw": ["GPU","FPGA","CPU"] + }, + { + "name": "database", + "requirements":[ + {"min_mem": 2, "max_mem": 16, "min_cpu": 1, "max_cpu":16, "max_instances":10} + ], + "variants": ["DOCKER","VM"], + "hw": ["CPU","GPU"] + } + ] + } +""" + +class ApplicationDataExtractor(): + def __init__(self): + self.json_url = None + self.application_data = {} + def setJSONUrl(self, url): + self.json_url = url + + def extractComponentData(self, _json): + if "Application_name" in _json: + self.application_data['application'] = _json['Application_name'] + else: + return False + self.application_data['components'] = [] + for component in _json['Application_components']: + comp = {'name': component['name'],'requirements':[{}],'variants':[],'hw':[]} + for res in component['Resources']: + if "CORES" in res: + comp['requirements'][0]['min_cpu'] = res['CORES'] + if "RAM" in res: + comp['requirements'][0]['min_mem'] = res['RAM'] + for variant in list_variants: + if variant in component['categories'] or variant in component['suggested_categories']: + comp['variants'].append(variant) + for hw in list_hws: + if hw in component['categories'] or hw in component['suggested_categories']: + comp['hw'].append(hw) + self.application_data['components'].append(comp) + + return self.application_data + + def parse(self): + if not self.json_url: + print("The url of the json is not set") + return None + _json = None + try: + _json = json.loads(open(self.json_url, "r").read()) + except Exception as e: + print("Could not open json file") + print(e) + return None + try: + return self.extractComponentData(_json) + except Exception as e: + print(e) + return None + +#####Silumation of the proactive module############# +@app.get("/node_candidates") +async def get_node_candidate(): + data = [ + {"name":"Node 1", "type": "normal","mem": 8, "vcpu": 4}, + {"name":"Node 2", "type": "normal","mem": 4, "vcpu": 2}, + {"name":"Node 1", "type": "gpu","mem": 8, "vcpu": 4}, + {"name":"Node 1", "type": "fpga","mem": 16, "vcpu": 0}, + ] + return {"status": True, "data": data} +#################################################### +@app.get("/") +def read_root(): + return {"Version": "1.0", "Maintainer":"Jean-Didir Totow <totow@unipi.gr>"} + +@app.post("/optimize") +async def optimize(message: OptimizationRequest): + try: + data = {'request': 'optimize', 'utility': message.utility} + publisher.setParameters(data, polymorphic_solver_topic) + publisher.send() + return {"status": True} + + except Exception as e: + return {"status": False, "message": e} + +@app.post("/groups") +async def collect(groups: ApplicationDataRequest): + app_data_extractor = ApplicationDataExtractor() + app_data_extractor.setJSONUrl(groups.json_path) + data = app_data_extractor.parse() + if not data: + return {"status": False, "message":"could not extract application data"} + data['request'] = 'application_data' + data['groups'] = groups.result + publisher.setParameters(data, polymorphic_solver_topic) + publisher.send() + return {"status": True} + + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=7879) \ No newline at end of file diff --git a/polymorphic_solver/src/app.py b/polymorphic_solver/src/app.py new file mode 100644 index 0000000000000000000000000000000000000000..b918359ce9d095dabfd34bf162ebfecf7dadeba5 --- /dev/null +++ b/polymorphic_solver/src/app.py @@ -0,0 +1,541 @@ +import os, time, json, logging, requests +from threading import Thread + +from env import Environment +from maddpg import MADDPG +from buffer import MultiAgentReplayBuffer +from example.app import Application +from morphemic import MorphemicArchetypeManager +import numpy as np +from amq_client.MorphemicConnection import Connection + +MIN_MEM = int(os.environ.get("MIN_MEM","1")) +MAX_MEM = int(os.environ.get("MAX_MEM","16")) +STEP_MEM = int(os.environ.get("STEP_MEM","1")) +MIN_CPU = int(os.environ.get("MIN_CPU","0")) +MAX_CPU = int(os.environ.get("MAX_CPU","8")) +STEP_CPU = int(os.environ.get("STEP_CPU","1")) +MAX_INSTANCES = int(os.environ.get("MAX_INSTANCES","10")) +HWS = os.environ.get("HWS","CPU,GPU,FPGA").split(",") +VARIANTS = os.environ.get("VARIANTS","SERVERLESS,VM,DOCKER").split(",") +PERFORMANCE_ACCEPTANCE = int(os.environ.get("PERFORMANCE_ACCEPTANCE","80")) +minimum_collection_size_analysis = int(os.environ.get("MINIMUM_COLLECTION_SIZE_ANALYSIS","200")) +proactive_scheduler_url = os.environ.get("PROACTIVE_URL","http://localhost:7879/node_candidates") + +activemq_port = int(os.environ.get("ACTIVEMQ_PORT", "61613")) +activemq_hostname = os.environ.get("ACTIVEMQ_HOST", "localhost") +activemq_topic = os.environ.get("ACTIVEMQ_TOPIC", "static-topic-1") +activemq_subs_key = os.environ.get("ACTIVEMQ_SUBS_KEY", "subs-1") +activemq_username = os.environ.get("ACTIVEMQ_USERNAME", "aaa") +activemq_password = os.environ.get("ACTIVEMQ_PASSWORD", "111") +polymorhic_solver_topic = os.environ.get("POLYMORPHIC_TOPIC","/topic/polymorphic_solver") +application_state_topic = os.environ.get("APPLICATION_STATE","/topic/application_state") + +logname = "./log/profiler.log" +logging.basicConfig(filename=logname,filemode='a',format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',datefmt='%H:%M:%S',level=logging.DEBUG) + + +class Listener(object): + def __init__(self, conn, handler): + self.conn = conn + self.count = 0 + self.handler = handler + self.start = time.time() + + def on_error(self, frame): + print("received an error %s" % frame.body) + + def on_message(self, header,frame): + print(header) + self.handler(frame) + +class Consumer(Thread): + def __init__(self, handler, queue): + self.handler = handler + self.queue = queue + self.conn = None + self._stop = False + super(Consumer,self).__init__() + + def stop(self): + self._stop = True + if self.conn: + self.conn.disconnect() + + def run(self): + connected = False + while not connected: + if self._stop: + break + try: + print('Subscribe to the topic {0}'.format(self.queue)) + logging.info('Subscribe to the topic {0}'.format(self.queue)) + self.conn = Connection(username=activemq_username, password=activemq_password, host=activemq_hostname,port=activemq_port, debug=False) + self.conn.connect() + self.conn.set_listener('', Listener(self.conn, self.handler)) + self.conn.subscribe(destination=self.queue, id=1000, ack='auto') + connected = True + except Exception as e: + print("Could not subscribe") + logging.error("Could not subscribe to the topic {0}".format(self.queue)) + print(e) + connected = False + +class ApplicationComponent(): + def __init__(self, component_name, application_name): + self.application_name = application_name + self.name = component_name + self.constraints = ComponentConstraint(component_name) + self.environment = None + self.status = False + self.metrics = [] + self.hardware_list = HWS #CPU, GPU, FPGA + self.variants = VARIANTS #DOCKER, VM, SERVERLESS + #self.rl_engine = RLearning(self.name) + + def prepareResource(self): + min_mem, max_mem, min_cpu, max_cpu, max_instances = None, None, None, None, None + min_mem = self.constraints.getResourceValue("MEM",">") + max_mem = self.constraints.getResourceValue("MEM","<") + min_cpu = self.constraints.getResourceValue("CPU",">") + max_cpu = self.constraints.getResourceValue("CPU","<") + max_instances = self.constraints.getResourceValue("INSTANCE","<") + + if not min_mem: + min_mem = MIN_MEM + if not max_mem: + max_mem = MAX_MEM + if not min_cpu: + min_cpu = MIN_CPU + if not max_cpu: + max_cpu = MAX_CPU + if not max_instances: + max_instances = MAX_INSTANCES + + return min_mem, max_mem, min_cpu, max_cpu, max_instances, self.hardware_list, self.variants + + def getApplication(self): + return self.application_name + + def getName(self): + return self.name + + def setVariants(self, variants): + self.variants = variants + + def setHWS(self, hw): + self.hardware_list = hw + + def setMetrics(self, metrics): + self.metrics = metrics + + def addConstraint(self, name, resource, _type): + self.constraints.addConstraint(name, resource, _type) + +class Constraint(): + def __init__(self): + self.name = None + self.resource_value = None + self.type = ">" + def setName(self, name): + self.name = name + def setResourceValue(self, resource_value): + self.resource_value = resource_value + def setType(self, _type): + self.type = _type + def getName(self): + return self.name + def getType(self): + return self.type + def getResourceValue(self): + return self.resource_value + +class ComponentConstraint(): + def __init__(self, name): + self.name = name + self.constraints = [] + + def addConstraint(self, name, resource_value, _type): + constraint = Constraint() + constraint.setName(name) + constraint.setResourceValue(resource_value) + constraint.setType(_type) + self.constraints.append(constraint) + #print("constraint on {0} of type {1} for value {2} is added".format(name, _type, resource_value)) + + def getResourceValue(self, _name, _type): + for constraint in self.constraints: + if constraint.getName() == _name and constraint.getType() == _type: + return constraint.getResourceValue() + return None + + def getConstraint(self, name): + for constraint in self.constraints: + if constraint.getName() == name: + return constraint + return None + +class VirtualApplication(): + def __init__(self): + self.name = None + self.components = [] + self.properties = {} + self.states = {} + self.performance = 0 + + def setApplicationName(self, name): + self.name = name + + def setComponentProperties(self, properties, name): + if name in self.components: + self.properties[name] = properties + + def setComponentState(self, component_name, state): + self.setComponentProperties(component_name, state.properties) + self.states[component_name] = state + + def getComponentProperties(self, name): + if name in self.properties: + return self.properties[name] + return None + + def setComponentName(self, names): + self.components = names + + def getComponentNames(self): + return self.components + + def getPerformance(self): + return self.performance + +class PolymorphicSolver(Thread): + def __init__(self): + super(PolymorphicSolver, self).__init__() + self.application_components = {} + self.env = Environment() + self.memory = None + self.maddpg_agents = None + self.application_data = None + self.episodes = 0 + self.best_score = None + self.max_optimization_time = 30 #in second + self.last_optimization = int(time.time()) + self.best_optimization_time = None + self.best_performance = None + self.archetype_manager = MorphemicArchetypeManager() + self.consumer_command = Consumer(self.handleData, polymorhic_solver_topic) + self.consumer_application_state = Consumer(self.handleData, application_state_topic) + self.virtual_application = VirtualApplication() + self.node_candidate_data = [] + + def handleData(self, data): + #i may add something here + try: + data = json.loads(data) + except Exception as e: + print('Could not parse json content') + print(e) + return None + if not 'request' in data: + return None + if data['request'] == 'application_data': + if self.setApplicationData(data): + self.getNodeCandidates() + self.createComponents() + self.prepareMultiRLEngine() + self.virtual_application.setApplicationName(self.application_data['application']) + self.env.setApplication(self.virtual_application, self.application_data) + if data['request'] == 'optimize': + #self.optimize() + pass + if data['request'] == 'state': + pass + + def getNodeCandidates(self): + status = False + timeout = 180 # 3 minutes + _start = time.time() + while not status: + try: + print("Trying to retrieve nodes candidates") + response = requests.get(url=proactive_scheduler_url).text + self.node_candidate_data = json.loads(response)['data'] + print('Node candidates collected') + status = True + except Exception as e: + print("Could not collect the node candidate") + if time.time() - timeout >= _start: + status = True + else: + time.sleep(5) + + def nodeCandidateBiggestCpu(self): + result = 0 + for node in self.node_candidate_data: + if node['vcpu'] > result: + result = node['vcpu'] + return result + + def nodeCandidateBiggestMem(self): + result = 0 + for node in self.node_candidate_data: + if node['mem'] > result: + result = node['mem'] + return result + + def setApplicationData(self, data): + self.application_data = data + if "components" in data: + for component in data["components"]: + if not component["name"] in self.application_components: + comp = ApplicationComponent(component["name"],data["application"]) + if "variants" in component: + comp.setVariants(component["variants"]) + if "hw" in component: + comp.setHWS(component["hw"]) + if "metrics" in component: + comp.setMetrics(component["metrics"]) + if "requirements" in component: + all_requirements = component["requirements"][0] + if "min_mem" in all_requirements: + comp.addConstraint("MEM",all_requirements["min_mem"],">") + else: + comp.addConstraint("MEM",MIN_MEM,">") + if "max_mem" in all_requirements: + comp.addConstraint("MEM",all_requirements["max_mem"],"<") + else: + comp.addConstraint("MEM",self.nodeCandidateBiggestMem(),"<") + if "min_cpu" in all_requirements: + comp.addConstraint("CPU",all_requirements["min_cpu"],">") + else: + comp.addConstraint("CPU",MIN_CPU,">") + if "max_cpu" in all_requirements: + comp.addConstraint("CPU",all_requirements["max_cpu"],"<") + else: + comp.addConstraint("CPU",self.nodeCandidateBiggestCpu(),"<") + if "max_instances" in all_requirements: + comp.addConstraint("INSTANCE",all_requirements["max_instances"],"<") + else: + comp.addConstraint("INSTANCE",MAX_INSTANCES,"<") + self.application_components[component["name"]] = comp + return True + + def createComponents(self): + index = 0 + for name, comp in self.application_components.items(): + min_mem, max_mem, min_cpu, max_cpu, max_instances, hardware_list, variants = comp.prepareResource() + self.env.createStates(name, min_mem, max_mem, min_cpu, max_cpu, max_instances, hardware_list, variants) + self.archetype_manager.createArchetypes(name, index, variants, hardware_list) + index +=1 + self.archetype_manager.setNumberOfComponents(index) + + def prepareMultiRLEngine(self): + n_agents = self.env.n_agents + actor_dims = [] + for i in range(n_agents): + actor_dims.append(self.env.observation_space[i].shape[0]) + critic_dims = sum(actor_dims) + # action space is a list of arrays, assume each agent has same action space + n_actions = self.env.action_space[0].n + self.maddpg_agents = MADDPG(actor_dims, critic_dims, n_agents, n_actions, fc1=64, fc2=64, alpha=0.01, beta=0.01, scenario="default_application",chkpt_dir='tmp/maddpg/') + self.memory = MultiAgentReplayBuffer(1000000, critic_dims, actor_dims, n_actions, n_agents, batch_size=1024) + print("Multi agents initialized ...") + + def transformPropertyToNPArray(self, properties): + #[MEM, CPU, INSTANCE, HW, VARIANT] + if properties[3] == "CPU": + properties[3] = 1 + if properties[3] == "GPU": + properties[3] = 2 + if properties[3] == "FPGA": + properties[3] = 3 + if properties[4] == "VM": + properties[4] = 1 + if properties[4] == "DOCKER": + properties[4] = 2 + if properties[4] == "SERVERLESS": + properties[4] = 3 + return np.array(properties).astype("double") + + def transformNPArrayToWorld(self, state): + for state_component in state: + if state_component.properties[3] == 1: + state_component.properties[3] = "CPU" + if state_component.properties[3] == 2: + state_component.properties[3] = "GPU" + if state_component.properties[3] == 3: + state_component.properties[3] = "FPGA" + if state_component.properties[4] == 1: + state_component.properties[4] = "VM" + if state_component.properties[4] == 2: + state_component.properties[4] = "DOCKER" + if state_component.properties[4] == 3: + state_component.properties[4] = "SERVERLESS" + return state + + def obs_list_to_state_vector(self, observation): + state = np.array([]) + for obs in observation: + state = np.concatenate([state, obs]) + return state.astype("double") + + def optimize(self, app): + state = app.getState() + load = app.getLoad() + performance = app.getPerformance() + states_id = app.getStatesIds() + print("Current workload=",load,', Current performance=', performance, ", states ids=", states_id) + obs = [] + for state_component in state: + obs.append(self.transformPropertyToNPArray(state_component.properties)) + + iteration = 0 + learning_interval = 100 + line_added = 0 + + score = 0 + score_history = [] + _start = int(time.time()) + self.env.setStartOptimization(_start) + end_type = '' + #while int(time.time()) - self.last_optimization < self.max_optimization_time: + while True: + actions = self.maddpg_agents.choose_action(obs) + state_, rewards, dones, infos = self.env.step(actions, self.transformNPArrayToWorld(state)) + #time.sleep(1) + #if performance > 90: + # print("Acceptable performance = {0} found in {1} iterations".format(performance,iteration_performance)) + # iteration_performance = 1 + score_history.append(sum(rewards)) + #score_history.append(all_rewards) + #print("Current workload => ",load,'Current performance => ', performance, "states ids =>", states_id) + ## + pf = 0 + for info in infos: + if 'performance' in info: + pf = info['performance'] + if pf > 50: + self.archetype_manager.addLine(state_,pf) + line_added +=1 + if line_added % 100 == 0: + pass + #self.archetype_manager.analyse() + ## + if self.best_performance == None or pf > self.best_performance: + self.best_performance = pf + obs_ = [] + for state_component in state_: + obs_.append(self.transformPropertyToNPArray(state_component.properties)) + + s_state = self.obs_list_to_state_vector(obs) + s_state_ = self.obs_list_to_state_vector(obs_) + + self.memory.store_transition(obs, s_state, actions, rewards, obs_, s_state_, dones) + state = state_ + + obs = [] + score += sum(rewards) + for state_component in state_: + obs.append(self.transformPropertyToNPArray(state_component.properties)) + + if iteration % learning_interval == 0: + self.maddpg_agents.learn(self.memory) + + iteration +=1 + + if iteration % learning_interval == 0: + self.maddpg_agents.learn(self.memory) + + if any(dones): + self.maddpg_agents.learn(self.memory) + for info in infos: + if 'info' in info: + end_type = info['info'] + break + #if total_steps % minimum_collection_size_analysis == 0: + # self.archetype_manager.analyse() + score_history.append(score) + avg_score = np.mean(score_history) + _duration = round(time.time() - _start,4) + + if self.best_optimization_time ==None or _duration < self.best_optimization_time: + self.best_optimization_time = _duration + + self.episodes +=1 + if self.best_score == None or avg_score > self.best_score: + self.best_score = avg_score + self.maddpg_agents.save_checkpoint() + + if self.episodes % 30 == 0: + time.sleep(10) + #performance = app.getPerformance() + print('Best performance=', self.best_performance,', best optimization time=',self.best_optimization_time,',episodes=', self.episodes, 'best score=', self.best_score,', duration=', round(_duration,4),' sec','iteration=',iteration) + #self.last_optimization = int(time.time()) + _file = open('metrics.csv','a') + _file.write('{0},{1},{2},{3},{4},{5},{6},{7}\n'.format(self.episodes,self.best_performance,self.best_optimization_time,avg_score,self.best_score,round(_duration,4),iteration, end_type)) + _file.close() + """ + if (self.best_optimization_time == 0) or (_duration < self.best_optimization_time) or (avg_score > self.best_score): + if avg_score > self.best_score: + self.best_score = avg_score + self.maddpg_agents.save_checkpoint() + if _duration > self.best_optimization_time: + self.best_optimization_time = _duration + self.maddpg_agents.save_checkpoint() """ + + def run(self): + self.consumer_command.start() + self.consumer_application_state.start() + #///////////////////////////////// + #simul_app = Application("test_application") + #state = self.env.getInitialState() + #simul_app.setState(state) + #simul_app.generateLoad() + #self.env.setApplication(simul_app, self.application_data) + self.env.setMaxOptimization(self.max_optimization_time) + #////////////////////////////////// + _file = open('./log/metrics.csv','a') + _file.write('Episode,Best performance,Best optimization time,Score,Best score,Duration,Iteration,Info\n') + _file.close() + while True: + time.sleep(30) + """ + performance = simul_app.getPerformance() + load = simul_app.getLoad() + if performance <= PERFORMANCE_ACCEPTANCE: + self.optimize(simul_app) + else: + print("No need of optimization ... performance = {0}, for load ={1}".format(performance,load)) + time.sleep(10) + simul_app.generateLoad() + """ + + + +if __name__ == "__main__": + solver = PolymorphicSolver() + """ + data = { + "application": "test_application", + "components":[ + { + "name": "web_service", + "requirements":[ + {"min_mem": 2, "max_mem": 16, "min_cpu": 1, "max_cpu":16, "max_instances":10} #[2,4,8,16] + ], + "variants": ["DOCKER","VM"], + "hw": ["GPU","FPGA","CPU"] + }, + { + "name": "database", + "requirements":[ + {"min_mem": 2, "max_mem": 16, "min_cpu": 1, "max_cpu":16, "max_instances":10} + ], + "variants": ["DOCKER","VM"], + "hw": ["CPU","GPU"] + } + ] + } + solver.setApplicationData(data) + """ + solver.start() diff --git a/polymorphic_solver/src/buffer.py b/polymorphic_solver/src/buffer.py new file mode 100644 index 0000000000000000000000000000000000000000..e2061ebb31d42f6f9f4122cc1f6a5b12c3434709 --- /dev/null +++ b/polymorphic_solver/src/buffer.py @@ -0,0 +1,83 @@ +import numpy as np + +class MultiAgentReplayBuffer: + def __init__(self, max_size, critic_dims, actor_dims, + n_actions, n_agents, batch_size): + self.mem_size = max_size + self.mem_cntr = 0 + self.n_agents = n_agents + self.actor_dims = actor_dims + self.batch_size = batch_size + self.n_actions = n_actions + + self.state_memory = np.zeros((self.mem_size, critic_dims)) + self.new_state_memory = np.zeros((self.mem_size, critic_dims)) + self.reward_memory = np.zeros((self.mem_size, n_agents)) + self.terminal_memory = np.zeros((self.mem_size, n_agents), dtype=bool) + + self.init_actor_memory() + + def init_actor_memory(self): + self.actor_state_memory = [] + self.actor_new_state_memory = [] + self.actor_action_memory = [] + + for i in range(self.n_agents): + self.actor_state_memory.append( + np.zeros((self.mem_size, self.actor_dims[i]))) + self.actor_new_state_memory.append( + np.zeros((self.mem_size, self.actor_dims[i]))) + self.actor_action_memory.append( + np.zeros((self.mem_size, self.n_actions))) + + + def store_transition(self, raw_obs, state, action, reward, + raw_obs_, state_, done): + # this introduces a bug: if we fill up the memory capacity and then + # zero out our actor memory, the critic will still have memories to access + # while the actor will have nothing but zeros to sample. Obviously + # not what we intend. + # In reality, there's no problem with just using the same index + # for both the actor and critic states. I'm not sure why I thought + # this was necessary in the first place. Sorry for the confusion! + + #if self.mem_cntr % self.mem_size == 0 and self.mem_cntr > 0: + # self.init_actor_memory() + + index = self.mem_cntr % self.mem_size + + for agent_idx in range(self.n_agents): + self.actor_state_memory[agent_idx][index] = raw_obs[agent_idx] + self.actor_new_state_memory[agent_idx][index] = raw_obs_[agent_idx] + self.actor_action_memory[agent_idx][index] = action[agent_idx] + + self.state_memory[index] = state + self.new_state_memory[index] = state_ + self.reward_memory[index] = reward + self.terminal_memory[index] = done + self.mem_cntr += 1 + + def sample_buffer(self): + max_mem = min(self.mem_cntr, self.mem_size) + + batch = np.random.choice(max_mem, self.batch_size, replace=False) + + states = self.state_memory[batch] + rewards = self.reward_memory[batch] + states_ = self.new_state_memory[batch] + terminal = self.terminal_memory[batch] + + actor_states = [] + actor_new_states = [] + actions = [] + for agent_idx in range(self.n_agents): + actor_states.append(self.actor_state_memory[agent_idx][batch]) + actor_new_states.append(self.actor_new_state_memory[agent_idx][batch]) + actions.append(self.actor_action_memory[agent_idx][batch]) + + return actor_states, states, actions, rewards, \ + actor_new_states, states_, terminal + + def ready(self): + if self.mem_cntr >= self.batch_size: + return True diff --git a/polymorphic_solver/src/env.py b/polymorphic_solver/src/env.py new file mode 100644 index 0000000000000000000000000000000000000000..9f09e6220fccd4e7e2aa2241ef33dacfa7c51981 --- /dev/null +++ b/polymorphic_solver/src/env.py @@ -0,0 +1,349 @@ +import numpy as np +import os, itertools, random, time +from gym.spaces import Box, Discrete +#from multiagent.environment import MultiAgentEnv +from multiagent.environment import MultiAgentEnv +from multiagent.core import World, Agent + +from gym import Env +from resource_logic import ResourceLogic + +MAX_MEM = int(os.environ.get("MAX_MEM","16")) +MIN_MEM = int(os.environ.get("MIN_MEM","1")) +STEP_MEM = int(os.environ.get("STEP_MEM","1")) +MIN_CPU = int(os.environ.get("MIN_CPU","0")) +MAX_CPU = int(os.environ.get("MAX_CPU","8")) +STEP_CPU = int(os.environ.get("STEP_CPU","1")) +MAX_INSTANCES = int(os.environ.get("MAX_INSTANCES","10")) + +action_names = [ + "SCALE_MEM_UP", + "SCALE_MEM_DOWN", + "SCALE_CPU_UP", + "SCALE_CPU_DOWN", + "SCALE_INSTANCE_UP", + "SCALE_INSTANCE_DOWN", + "MAKE_DOCKER_CONTAINER", + "MAKE_VM", + "MAKE_SERVERLESS", + "RUN_ON_GPU", + "RUN_ON_FPGA", + "DOUBLE_MEM", + "DEVIDE_MEM", + "DOUBLE_CPU", + "DEVIDE_CPU", + "DOUBLE_INSTANCES", + "DEVIDE_INSTANCES", + "RUN_ON_CPU", + "MEM_UP_CPU_UP", # + "MEM_UP_CPU_DOWN", + "MEM_DOWN_CPU_UP", + "MEM_DOWN_CPU_DOWN", + "MEM_UP_CPU_UP_INSTANCE_UP", + "MEM_UP_CPU_UP_INSTANCE_DOWN", + "MEM_UP_CPU_DOWN_INSTANCE_UP", + "MEM_UP_CPU_DOWN_INSTANCE_DOWN", + "MEM_DOWN_CPU_UP_INSTANCE_UP", + "MEM_DOWN_CPU_UP_INSTANCE_DOWN", + "MEM_DOWN_CPU_DOWN_INSTANCE_UP", + "MEM_DOWN_CPU_DOWN_INSTANCE_DOWN" +] + +class State(): + def __init__(self, id, properties, metrics={}): + self.id = id + self.properties = properties #(MEM, CPU, INSTANCE, HW, VARIANT) + self.metrics = metrics + + def isValid(self): + #if GPU or FPGA there is no need to have a cpu + if self.properties[3] == 'GPU' or self.properties[3] == 'FPGA': + if self.properties[1] > 0: + return False + if self.properties[3] == 'CPU': + if self.properties[1] == 0: + return False + if self.properties[4] == 'SERVERLESS': + if self.properties[3] == 'FPGA': + return False + return True + + def getId(self): + return self.id + + def __repr__(self): + return "State ID = {0}, Properties = {1}".format(self.id, self.properties) + +class Environment(Env): + def __init__(self): + self.n_actions = len(action_names) + self.action_space = [] + self.observation_space = [] + self.n_agents = 0 + self.multi_agents_env = None + self.components = [] + self.resource_logics = {} + self.application = {} + self.states = {} + self.states_map = {} + self.visitation_map = {} + self.index_argmax = {} + self.list_index_argmax = [] + self.repetition_index = 0 + self.max_optimization_time = 0 + self.start_optimization_time = 0 + + def setApplication(self, app, data): + self.application['data'] = data + self.application['object'] = app + for component in data['components']: + self.components.append(component['name']) + + def setMaxOptimization(self, _time): + self.max_optimization_time = _time + + def setStartOptimization(self, _start_time): + self.start_optimization_time = _start_time + + def createStates(self,name, min_mem, max_mem, min_cpu, max_cpu, max_instances, hws, variants): + print("Creating states for component {0} ...".format(name)) + self.states[name] = [] + self.index_argmax[name] = 0 + resource_logic = ResourceLogic(min_mem, max_mem, min_cpu, max_cpu, max_instances) + self.resource_logics[name] = resource_logic + _mem = resource_logic.getMemList() + _cpu = resource_logic.getCPUList() + _instances = resource_logic.getNumberInstancesList() + _all_list = [_mem, _cpu, _instances] + if len(hws) == 0: + hws.append('CPU') + _all_list.append(hws) + if len(variants) == 0: + variants.append('VM') + _all_list.append(variants) + + _list = list(np.arange(0,1.1,0.1).round(2)) #application workload + _all_list.append(_list) + + index = 0 + self.states_map[name] = {} + for properties in itertools.product(*_all_list): + state = State(index, list(properties)) + if state.isValid(): + self.states[name].append(state) + self.states_map[name][self.transformPropToString(properties)] = index + index +=1 + #self.observation_space.append(Box(low=np.array([0]), high=np.array([index]), dtype=np.float32)) + self.observation_space.append(Box(low=np.float(0), high=np.float(len(_all_list)), shape=(len(_all_list),), dtype=np.float32)) + #self.observation_space = Box(low=np.float(0), high=np.float(index), shape=(2,)) + print("{0} states created for component {1}".format(index, name)) + self.n_agents +=1 + self.action_space.append(Discrete(self.n_actions)) + + def getNextState(self, action, _state, name): + properties = _state.properties + if action == 0 or action == 1 or action == 11 or action == 12: #MEM SCALE + mem = self.resource_logics[name].getMemSize(properties[0], action_names[action]) + if mem != None: + properties[0] = int(mem) + elif action == 2 or action == 3 or action == 13 or action == 14: #CPU SCALE + if properties[3] == 'FPGA' or properties[3] == 'GPU': + properties[1] = 0 + else: + cpu = self.resource_logics[name].getNumberCPU(properties[1], action_names[action]) + if cpu != None: + properties[1] = int(cpu) + elif action == 4: + if properties[2] != self.resource_logics[name].max_instances: + properties[2] +=1 + elif action == 5 or action == 6 or action == 15 or action == 16: #INSTANCE SCALE + new_n_instances = self.resource_logics[name].getNumberInstances(properties[2], action_names[action]) + if new_n_instances != None: + properties[2] = int(new_n_instances) + elif action == 6: #MAKE DOCKER CONTAINER + #if properties[4] != 'DOCKER': + properties[4] = 'DOCKER' + elif action == 7: #MAKE VM + #if properties[4] != 'VM': + properties[4] = 'VM' + elif action == 8: #MAKE SERVERLESS + #if properties[4] != 'SERVERLESS': + properties[4] = 'SERVERLESS' + if properties[3] == 'FPGA': + properties[3] = 'CPU' + properties[1] = self.resource_logics[name].min_mem + elif action == 9: #RUN ON GPU + #if properties[3] != 'GPU': + properties[3] = 'GPU' + #properties[0] = self.resource_logics[name].min_mem + #properties[2] = 1 + properties[1] = 0 + if properties[4] == 'SERVERLESS': + properties[4] = 'VM' + elif action == 10: #RUN ON FPGA + properties[3] = 'FPGA' + #properties[0] = self.resource_logics[name].min_mem + #properties[2] = 1 + properties[1] = 0 + if properties[4] == 'SERVERLESS': + properties[4] = 'VM' + elif action == 17: + if properties[3] != 'CPU': + properties[3] = 'CPU' + properties[1] = self.resource_logics[name].min_cpu + elif action == 18 or action == 19 or action == 20 or action == 21: #MEM, CPU + if properties[3] == 'CPU': + ok, mem, cpu = self.resource_logics[name].getMemCPU(properties[0], properties[1], action_names[action]) + if ok: + properties[0] = mem + properties[1] = cpu + elif action == 22 or action == 23 or action == 24 or action == 25 or action == 26 or action == 27 or action == 28 or action == 29: + if properties[3] == 'CPU': + ok, mem, cpu, instances = self.resource_logics[name].getMemCpuInstances(properties[0], properties[1], properties[2], action_names[action]) + if ok: + properties[0] = mem + properties[1] = cpu + properties[2] = instances + else: + pass + #push the current configuration + #self.application.setConfig(properties) + #get current utility values + #properties[5] = round(self.application['object'].exportWorkload(),1) + return self.findState(properties, name) + + def findState(self, properties,name): + prop_str = self.transformPropToString(properties) + if prop_str in self.states_map[name]: + index = self.states_map[name][prop_str] + return self.states[name][index] + return None + + def transformPropToString(self,properties): + result = "" + for prop in properties: + result += str(prop) + return result + + def componentVariantsSupported(self, name): + for component in self.application['data']['components']: + if component['name'] == name: + return component['variants'] + return [] + + def componentHWSupported(self, name): + for component in self.application['data']['components']: + if component['name'] == name: + return component['hw'] + return [] + + def step(self, actions, states): + new_states = [] + rewards = [] + dones = [] + infos = [] + i = 0 #count the number of agents (number of application components) + + for component_state in actions: + self.list_index_argmax = np.argsort(component_state) + action = self.list_index_argmax[self.index_argmax[self.components[i]]] + action_name = action_names[action] + if action == 6: #DOCKER + if not "DOCKER" in self.componentVariantsSupported(self.components[i]): + self.index_argmax[self.components[i]] +=1 + return self.step(actions, states) + if action == 7: #VM + if not "VM" in self.componentVariantsSupported(self.components[i]): + self.index_argmax[self.components[i]] +=1 + return self.step(actions, states) + if action == 8: #SERVERLESS + if not "SERVERLESS" in self.componentVariantsSupported(self.components[i]): + self.index_argmax[self.components[i]] +=1 + return self.step(actions, states) + if action == 9: #GPU + if not "GPU" in self.componentHWSupported(self.components[i]): + self.index_argmax[self.components[i]] +=1 + return self.step(actions, states) + if action == 10: #FPGA + if not "FPGA" in self.componentHWSupported(self.components[i]): + self.index_argmax[self.components[i]] +=1 + return self.step(actions, states) + #print("Action for {0} is {1}".format(self.components[i], action_name)) + done = False + info = {} + reward = 0 + + if action >= len(action_names): + action = 0 + #self.state.properties[0] = MIN_MEM + #self.state.properties[2] = 1 + new_state = self.getNextState(action, states[i], self.components[i]) + if new_state == None: + #print("State {0} does not exist for the component {1}".format(states[i].properties, self.components[i])) + _state = self.getRandomState() + self.index_argmax[self.components[i]] = 0 + states[i] = _state[i] + return self.step(actions, states) + key = "{0}-{1}-{2}-{3}".format(states[i].id, new_state.id, action, self.components[i]) + if key in self.visitation_map: + _state = self.getRandomState() + self.index_argmax[self.components[i]] = 0 + if self.repetition_index >= len(self.list_index_argmax): + self.index_argmax[self.components[i]] +=1 + print("Repetition index = {0} reached the limit".format(self.repetition_index)) + self.repetition_index = 0 + infos = [{'info': 'pitch'}] * len(self.components) + rewards = [np.float(-100)] * len(self.components) + dones = [True] * len(self.components) + return states, rewards, dones, infos + states[i] = _state[i] + self.repetition_index +=1 + return self.step(actions, states) #avoiding repetitive visitation + else: + self.visitation_map[key] = True + self.index_argmax[self.components[i]] = 0 + self.repetition_index = 0 + #print("Previous state", states[i].properties, "Current state", new_state.properties) + self.application['object'].setComponentState(self.components[i], new_state) + new_states.append(new_state) + current_performance = self.application['object'].getPerformance() + #current_performance_wn = self.application['object'].getPerformanceWithoutNormalization() + #print("Current performance = {0}, new states = {1}".format(current_performance, new_states)) + info['performance'] = current_performance + #if current_performance_wn > previous_performance_wn: + # reward = current_performance * 2 + #else: + # reward = -1 * ((100 - current_performance) * 2) + reward = int(current_performance * 100) + if current_performance >= 95: #0.9 + done = True + info['info'] = 'goal' + if time.time() - self.start_optimization_time >= self.max_optimization_time: + done = True + info['info'] = 'timeout' + rewards.append(np.float(reward)) + dones.append(done) + infos.append(info) + i +=1 + #if self.previous_state != None and self.previous_state.id == self.state.id: + # action +=1 + # return self.step(action) + #self.iteration +=1 + return new_states, rewards, dones, infos + #return new_state, reward, done, info + def getRandomState(self): + result = [] + for component in self.states: + index = random.randint(0, len(self.states[component])-1) + result.append(self.states[component][index]) + return result + + def getInitialState(self): + result = {} + for component in self.states: + result[component] = self.states[component][0] + return result + + def reset(self): + return None + \ No newline at end of file diff --git a/polymorphic_solver/src/environment.py b/polymorphic_solver/src/environment.py new file mode 100644 index 0000000000000000000000000000000000000000..f0ba9db73587a189a48535b3e1aaf19d3a9ada8f --- /dev/null +++ b/polymorphic_solver/src/environment.py @@ -0,0 +1,56 @@ +import os, time, logging +import numpy as np + +MAX_MEM = int(os.environ.get("MAX_MEM","16")) +MIN_MEM = int(os.environ.get("MIN_MEM","1")) +STEP_MEM = int(os.environ.get("STEP_MEM","1")) +MIN_CPU = int(os.environ.get("MIN_CPU","0")) +MAX_CPU = int(os.environ.get("MAX_CPU","8")) +STEP_CPU = int(os.environ.get("STEP_CPU","1")) +MAX_INSTANCES = int(os.environ.get("MAX_INSTANCES","10")) +MEM_LIMIT = int(os.environ.get("MEM_LIMIT", "50000")) +STEPS = int(os.environ.get("STEPS","50000")) +WINDOW_LENGHT = int(os.environ.get("WINDOW_LENGHT","1")) +NB_STEPS_WARMUP = int(os.environ.get("NB_STEPS_WARMUP","10")) +MIN_PERFORMANCE = int(os.environ.get("MIN_PERFORMANCE","720")) +MAX_PERFORMANCE = int(os.environ.get("MAX_PERFORMANCE","1870")) +TARGET_MODEL_UPDATE = 1e-2 +BATCH_SIZE = int(os.environ.get("BATCH_SIZE","1024")) + +action_names = [ + "SCALE_MEM_UP", + "SCALE_MEM_DOWN", + "SCALE_CPU_UP", + "SCALE_CPU_DOWN", + "SCALE_INSTANCE_UP", + "SCALE_INSTANCE_DOWN", + "MAKE_DOCKER_CONTAINER", + "MAKE_VM", + "MAKE_SERVERLESS", + "RUN_ON_GPU", + "RUN_ON_FPGA", + "DOUBLE_MEM", + "DIVIDE_MEM", + "DOUBLE_CPU", + "DIVIDE_CPU", + "DOUBLE_INSTANCES", + "DIVIDE_INSTANCES", + "RUN_ON_CPU", + "MEM_UP_CPU_UP", # + "MEM_UP_CPU_DOWN", + "MEM_DOWN_CPU_UP", + "MEM_DOWN_CPU_DOWN", + "MEM_UP_CPU_UP_INSTANCE_UP", + "MEM_UP_CPU_UP_INSTANCE_DOWN", + "MEM_UP_CPU_DOWN_INSTANCE_UP", + "MEM_UP_CPU_DOWN_INSTANCE_DOWN", + "MEM_DOWN_CPU_UP_INSTANCE_UP", + "MEM_DOWN_CPU_UP_INSTANCE_DOWN", + "MEM_DOWN_CPU_DOWN_INSTANCE_UP", + "MEM_DOWN_CPU_DOWN_INSTANCE_DOWN" +] + +logger = logging.getLogger(__name__) + +class Environment(): + def __init__(self, ) \ No newline at end of file diff --git a/polymorphic_solver/src/example/.DS_Store b/polymorphic_solver/src/example/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2556a1cc1cebdeec00a2ddcedce1b705e969f5ce Binary files /dev/null and b/polymorphic_solver/src/example/.DS_Store differ diff --git a/polymorphic_solver/src/example/__pycache__/app.cpython-35.pyc b/polymorphic_solver/src/example/__pycache__/app.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cab01c4f72ed17e41dcf179bbf9815f667242b31 Binary files /dev/null and b/polymorphic_solver/src/example/__pycache__/app.cpython-35.pyc differ diff --git a/polymorphic_solver/src/example/__pycache__/app.cpython-36.pyc b/polymorphic_solver/src/example/__pycache__/app.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c034740777f328a6874c8fe056481b45fbf6d86 Binary files /dev/null and b/polymorphic_solver/src/example/__pycache__/app.cpython-36.pyc differ diff --git a/polymorphic_solver/src/example/__pycache__/app.cpython-39.pyc b/polymorphic_solver/src/example/__pycache__/app.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2fc3b1356d74ab124e33e51f1da3ecc806a7bc30 Binary files /dev/null and b/polymorphic_solver/src/example/__pycache__/app.cpython-39.pyc differ diff --git a/polymorphic_solver/src/example/__pycache__/simul.cpython-39.pyc b/polymorphic_solver/src/example/__pycache__/simul.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe5a67584720c6b7b9aa6abff34e1fca802a3d41 Binary files /dev/null and b/polymorphic_solver/src/example/__pycache__/simul.cpython-39.pyc differ diff --git a/polymorphic_solver/src/example/app.py b/polymorphic_solver/src/example/app.py new file mode 100644 index 0000000000000000000000000000000000000000..337e2a1d8124cb0d09beee3fe432d7424d0ce251 --- /dev/null +++ b/polymorphic_solver/src/example/app.py @@ -0,0 +1,100 @@ +import time, random +from threading import Thread +import random + +#computing power +computing_power = { + 'CPU': 1, + 'GPU': 20, + 'FPGA': 25 +} +class Database(): + def __init__(self): + self.state = None + self.response_time = 0 + self.throughput = 0 + def getState(self): + return self.state + def getProperties(self): + return self.state.properties + def getResponseTime(self): + return self.response_time + def getThroughput(self): + return self.throughput + def setState(self, state): + self.state = state + self.computeKPI() + def computeKPI(self): + if self.state.properties[1] > 0: + self.response_time = 2000 / (computing_power['CPU'] * self.state.properties[1] * self.state.properties[2] * self.state.properties[0]) + self.throughput = computing_power['CPU'] * self.state.properties[1] + if self.state.properties[3] == 'GPU': + self.response_time = 2000 / (computing_power['GPU'] * self.state.properties[2] * self.state.properties[0]) + self.throughput = 100 + if self.state.properties[3] == 'FPGA': + self.response_time = 2000 / (computing_power['FPGA'] * self.state.properties[2] * self.state.properties[0]) + self.throughput = 125 + if self.state.properties[4] == 'DOCKER': + self.response_time *= 1.2 + if self.state.properties[4] == 'SERVERLESS': + self.response_time *= 1.5 + + +class WebService(): + def __init__(self): + self.state = None + self.response_time = 0 + self.number_request = 0 + def getState(self): + return self.state + def getProperties(self): + return self.state.properties + def getResponseTime(self): + return self.response_time + def getNumberRequest(self): + return self.number_request + def setState(self, state): + self.state = state + self.computeKPI() + def computeKPI(self): + if self.number_request < 10: + self.response_time = 1 #ms + else: + if self.state.properties[1] > 0: + self.response_time = (self.number_request * 2000) / (computing_power['CPU'] * self.state.properties[1] * self.state.properties[2] * self.state.properties[0]) + if self.state.properties[3] == 'GPU': + self.response_time = (self.number_request * 2000) / (computing_power['GPU'] * self.state.properties[2] * self.state.properties[0]) + if self.state.properties[3] == 'FPGA': + self.response_time = (self.number_request * 2000) / (computing_power['FPGA'] * self.state.properties[2] * self.state.properties[0]) + if self.state.properties[4] == 'DOCKER': + self.response_time *= 1.2 + if self.state.properties[4] == 'SERVERLESS': + self.response_time *= 1.5 + +class Application(): + def __init__(self, name): + self.name = name + self.database = Database() + self.web_service = WebService() + self.performance = 0 + + def setState(self, name, state): + if name == "database": + self.database.setState(state) + if name == "webservice": + self.web_service.setState(state) + + def getState(self): + return {"database": self.database.getState(),"webservice": self.web_service.getState()} + def computePerformance(self): + ws_response_time = self.web_service.getResponseTime() + db_response_time = self.database.getResponseTime() + #ws_number_request = self.web_service.getNumberRequest() #load + db_throughput = self.database.getThroughput() + self.performance = db_throughput / (ws_response_time * db_response_time) + + def getPerformance(self): + return self.performance + + + diff --git a/polymorphic_solver/src/ingestor.py b/polymorphic_solver/src/ingestor.py new file mode 100644 index 0000000000000000000000000000000000000000..078a5e908202963c26ec3bc56d7593290a1efb5c --- /dev/null +++ b/polymorphic_solver/src/ingestor.py @@ -0,0 +1,126 @@ +import time, json, logging, os +from threading import Thread + +from amq_client.MorphemicConnection import Connection + +activemq_hostname = os.environ.get("ACTIVEMQ_HOST", "localhost") +activemq_port = int(os.environ.get("ACTIVEMQ_PORT", "61613")) +activemq_topic = os.environ.get("ACTIVEMQ_TOPIC", "static-topic-1") +activemq_subs_key = os.environ.get("ACTIVEMQ_SUBS_KEY", "subs-1") +activemq_username = os.environ.get("ACTIVEMQ_USERNAME", "aaa") +activemq_password = os.environ.get("ACTIVEMQ_PASSWORD", "111") + +polymorhic_solver_topic = os.environ.get("POLYMORPHIC_TOPIC","polymorphic_solver") +cdo_folder_path = os.environ.get("CDO_FOLDER_PATH","") + + +logname = "./log/solver.log" +logging.basicConfig(filename=logname,filemode='a',format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',datefmt='%H:%M:%S',level=logging.DEBUG) + +class Publisher(Thread): + def __init__(self): + self.message = None + self.destination = None + self.client = None + super(Publisher, self).__init__() + + def setParameters(self, message, queue): + self.message = message + self.queue = queue + + def run(self): + self.connect() + while True: + time.sleep(2) + + def connect(self): + while True: + try: + print('The publisher tries to connect to ActiveMQ broker') + logging.info('The publisher tries to connect to ActiveMQ broker') + self.client = Connection(username=activemq_username, password=activemq_password, host=activemq_hostname,port=activemq_port, debug=False) + self.client.connect() + print("connection established") + logging.info("connection established") + return True + except: + print("Could not connect the publisher") + logging.error("Could not connect the publisher") + + def send(self): + if self.message == None or self.queue == None: + print("Message or queue is None") + return False + try: + #self.client.send(body=json.dumps(self.message), destination=self.queue, persistent='false', auto_content_length=False, content_type="application/json") + self.client.send_to_topic(self.queue, self.message) + return True + except Exception as e: + print(e) + self.client.disconnect() + print("Reconnection in 10s ...") + logging.info("Reconnection in 10s ...") + time.sleep(10) + self.connect() + self.send() + +class ComponentDeploymentConfiguration(): + def __init__(self): + self.name = None + self.memory = None + self.vcpu = None + self.variant = None + self.hw = None + self.instances = None + self.time = int(time.time()) + def setParameters(self, name, memory, vcpu, variant, hw, instances): + self.name = name + self.memory = memory + self.vcpu = vcpu + self.variant = variant + self.hw = hw + self.instances = instances + if self.vcpu > 0: + self.hw = 'CPU' + + def export(self): + return { + 'name': self.name, + 'memory': self.memory, + 'cpu': self.vcpu, + 'variant': self.variant, + 'hw': self.hw, + 'instances': self.instances, + 'time': self.time + } + +class Ingestor(Thread): + def __init__(self): + self.application = None + self.deployments_configs = {} + self.publisher = Publisher() + super(Ingestor, self).__init__() + + def setApplicationName(self, name): + self.application = name + def getApplicationName(self): + return self.application + def makeKey(self, name, variant, hw, memory, vcpu, instances): + return "{0}_{1}_{2}_{3}_{4}_{5}".format(name, variant, hw, memory,vcpu,instances) + + def newDeploymentConfiguration(self, name, variant, hw, memory, vcpu, instances): + key = self.makeKey(name,variant,hw,memory,vcpu) + if not key in self.deployments_configs: + config = ComponentDeploymentConfiguration() + config.setParameters(name,memory,vcpu,variant,hw,instances) + self.deployments_configs[key] = config + self.publisher.setParameters(config.export(), polymorhic_solver_topic) + self.publisher.send() + + def readXMLFile(self): + pass + + def run(self): + while True: + self.readXMLFile() + time.sleep(10) \ No newline at end of file diff --git a/polymorphic_solver/src/json/configs.json b/polymorphic_solver/src/json/configs.json new file mode 100644 index 0000000000000000000000000000000000000000..9aef1a76d82a16dfc1c08631a657e73fe2c44b1b --- /dev/null +++ b/polymorphic_solver/src/json/configs.json @@ -0,0 +1,22 @@ +{ + "Application_name":"Network5G", + "Number_of_components":2, + "Application_components":[ + { + "name":"web_service", + "Resources": [{"CORES":2}, {"RAM":2 }], + "categories": ["DOCKET","VM","SERVERLESS"], + "repository": "https://github.com/Mailu/Mailu", + "suggested_categories":["FPGA"], + "language":"Python" + }, + { + "name":"database", + "Resources": [{"CORES":4}, {"RAM":4 }], + "categories": ["DOCKER","VM"], + "repository": "https://github.com/Mailu/Mailu", + "suggested_categories":["GPU"], + "language":"C++" + } + ] +} \ No newline at end of file diff --git a/polymorphic_solver/src/maddpg.py b/polymorphic_solver/src/maddpg.py new file mode 100644 index 0000000000000000000000000000000000000000..cd5c0aeb8fcb2310d78708a4d84bcdc7257595c9 --- /dev/null +++ b/polymorphic_solver/src/maddpg.py @@ -0,0 +1,89 @@ +import torch as T +import torch.nn.functional as F +from agent import Agent + +class MADDPG: + def __init__(self, actor_dims, critic_dims, n_agents, n_actions, + scenario='simple', alpha=0.01, beta=0.01, fc1=64, + fc2=64, gamma=0.99, tau=0.01, chkpt_dir='tmp/maddpg/'): + self.agents = [] + self.n_agents = n_agents + self.n_actions = n_actions + chkpt_dir += scenario + for agent_idx in range(self.n_agents): + self.agents.append(Agent(actor_dims[agent_idx], critic_dims, n_actions, n_agents, agent_idx, alpha=alpha, beta=beta,chkpt_dir=chkpt_dir)) + + self.load_checkpoint() + + def save_checkpoint(self): + print('... saving checkpoint ...') + for agent in self.agents: + agent.save_models() + + def load_checkpoint(self): + print('... loading checkpoint ...') + for agent in self.agents: + agent.load_models() + + def choose_action(self, raw_obs): + actions = [] + for agent_idx, agent in enumerate(self.agents): + action = agent.choose_action(raw_obs[agent_idx]) + actions.append(action) + return actions + + def learn(self, memory): + if not memory.ready(): + return + + actor_states, states, actions, rewards, \ + actor_new_states, states_, dones = memory.sample_buffer() + + device = self.agents[0].actor.device + + states = T.tensor(states, dtype=T.float).to(device) + actions = T.tensor(actions, dtype=T.float).to(device) + rewards = T.tensor(rewards).to(device) + states_ = T.tensor(states_, dtype=T.float).to(device) + dones = T.tensor(dones).to(device) + + all_agents_new_actions = [] + all_agents_new_mu_actions = [] + old_agents_actions = [] + + for agent_idx, agent in enumerate(self.agents): + new_states = T.tensor(actor_new_states[agent_idx], + dtype=T.float).to(device) + + new_pi = agent.target_actor.forward(new_states) + + all_agents_new_actions.append(new_pi) + mu_states = T.tensor(actor_states[agent_idx], + dtype=T.float).to(device) + pi = agent.actor.forward(mu_states) + all_agents_new_mu_actions.append(pi) + old_agents_actions.append(actions[agent_idx]) + + new_actions = T.cat([acts for acts in all_agents_new_actions], dim=1) + mu = T.cat([acts for acts in all_agents_new_mu_actions], dim=1) + old_actions = T.cat([acts for acts in old_agents_actions],dim=1) + + for agent_idx, agent in enumerate(self.agents): + agent.actor.optimizer.zero_grad() + for agent_idx, agent in enumerate(self.agents): + critic_value_ = agent.target_critic.forward(states_, new_actions).flatten() + critic_value_[dones[:,0]] = 0.0 + critic_value = agent.critic.forward(states, old_actions).flatten() + + target = rewards[:,agent_idx] + agent.gamma*critic_value_ + critic_loss = F.mse_loss(target, critic_value) + agent.critic.optimizer.zero_grad() + critic_loss.backward(retain_graph=True) + agent.critic.optimizer.step() + + actor_loss = agent.critic.forward(states, mu).flatten() + actor_loss = -T.mean(actor_loss) + actor_loss.backward(retain_graph=True) + for agent_idx, agent in enumerate(self.agents): + agent.actor.optimizer.step() + agent.update_network_parameters() \ No newline at end of file diff --git a/polymorphic_solver/src/main.py b/polymorphic_solver/src/main.py new file mode 100644 index 0000000000000000000000000000000000000000..c15094af35ffdfb63bc47f43d4b3f0017b7475b8 --- /dev/null +++ b/polymorphic_solver/src/main.py @@ -0,0 +1,78 @@ +import numpy as np +from maddpg import MADDPG +from buffer import MultiAgentReplayBuffer +from make_env import make_env + +def obs_list_to_state_vector(observation): + state = np.array([]) + for obs in observation: + state = np.concatenate([state, obs]) + return state + +if __name__ == '__main__': + #scenario = 'simple' + scenario = 'simple_adversary' + env = make_env(scenario) + n_agents = env.n + actor_dims = [] + for i in range(n_agents): + actor_dims.append(env.observation_space[i].shape[0]) + critic_dims = sum(actor_dims) + + # action space is a list of arrays, assume each agent has same action space + n_actions = env.action_space[0].n + maddpg_agents = MADDPG(actor_dims, critic_dims, n_agents, n_actions, + fc1=64, fc2=64, + alpha=0.01, beta=0.01, scenario=scenario, + chkpt_dir='tmp/maddpg/') + + memory = MultiAgentReplayBuffer(1000000, critic_dims, actor_dims, n_actions, n_agents, batch_size=1024) + + PRINT_INTERVAL = 500 + N_GAMES = 50000 + MAX_STEPS = 25 + total_steps = 0 + score_history = [] + evaluate = False + best_score = 0 + + if evaluate: + maddpg_agents.load_checkpoint() + + for i in range(N_GAMES): + obs = env.reset() + score = 0 + done = [False]*n_agents + episode_step = 0 + while not any(done): + if evaluate: + env.render() + #time.sleep(0.1) # to slow down the action for the video + actions = maddpg_agents.choose_action(obs) + obs_, reward, done, info = env.step(actions) + + state = obs_list_to_state_vector(obs) + state_ = obs_list_to_state_vector(obs_) + + if episode_step >= MAX_STEPS: + done = [True]*n_agents + + memory.store_transition(obs, state, actions, reward, obs_, state_, done) + + if total_steps % 100 == 0 and not evaluate: + maddpg_agents.learn(memory) + + obs = obs_ + + score += sum(reward) + total_steps += 1 + episode_step += 1 + + score_history.append(score) + avg_score = np.mean(score_history[-100:]) + if not evaluate: + if avg_score > best_score: + maddpg_agents.save_checkpoint() + best_score = avg_score + if i % PRINT_INTERVAL == 0 and i > 0: + print('episode', i, 'average score {:.1f}'.format(avg_score)) diff --git a/polymorphic_solver/src/make_env.py b/polymorphic_solver/src/make_env.py new file mode 100644 index 0000000000000000000000000000000000000000..12c1ae946ce8fa6bfdb32652e11e803571beae6c --- /dev/null +++ b/polymorphic_solver/src/make_env.py @@ -0,0 +1,44 @@ +""" +Code for creating a multiagent environment with one of the scenarios listed +in ./scenarios/. +Can be called by using, for example: + env = make_env('simple_speaker_listener') +After producing the env object, can be used similarly to an OpenAI gym +environment. + +A policy using this environment must output actions in the form of a list +for all agents. Each element of the list should be a numpy array, +of size (env.world.dim_p + env.world.dim_c, 1). Physical actions precede +communication actions in this array. See environment.py for more details. +""" + +def make_env(scenario_name, benchmark=False): + ''' + Creates a MultiAgentEnv object as env. This can be used similar to a gym + environment by calling env.reset() and env.step(). + Use env.render() to view the environment on the screen. + + Input: + scenario_name : name of the scenario from ./scenarios/ to be Returns + (without the .py extension) + benchmark : whether you want to produce benchmarking data + (usually only done during evaluation) + + Some useful env properties (see environment.py): + .observation_space : Returns the observation space for each agent + .action_space : Returns the action space for each agent + .n : Returns the number of Agents + ''' + from multiagent.environment import MultiAgentEnv + import multiagent.scenarios as scenarios + + # load scenario from script + scenario = scenarios.load(scenario_name + ".py").Scenario() + # create world + world = scenario.make_world() + # create multiagent environment + if benchmark: + env = MultiAgentEnv(world, scenario.reset_world, scenario.reward, scenario.observation, scenario.benchmark_data) + else: + env = MultiAgentEnv(world, scenario.reset_world, scenario.reward, scenario.observation) + return env diff --git a/polymorphic_solver/src/metrics.csv b/polymorphic_solver/src/metrics.csv new file mode 100644 index 0000000000000000000000000000000000000000..ca9e4c5099b9c27c3900addb1e815cc00036f58e --- /dev/null +++ b/polymorphic_solver/src/metrics.csv @@ -0,0 +1,148 @@ +Episode,Best performance,Best optimization time,Score,Best score,Duration,Iteration,Info +1,0,30.095,0.1111285389381225,0.1111285389381225,30.095,25501,timeout +2,0,30.0944,1.9300055551146735,1.9300055551146735,30.0944,25201,timeout +3,3,30.0552,7.934560825915539,7.934560825915539,30.0552,24602,timeout +4,3,30.0422,3.2648044692737432,7.934560825915539,30.0422,23269,timeout +5,3,30.0422,0.32849200534413653,7.934560825915539,30.0445,23202,timeout +6,3,30.0422,0.1908501506711716,7.934560825915539,30.1259,21901,timeout +7,3,30.0422,0.7977648636566831,7.934560825915539,30.0522,22369,timeout +8,3,30.0422,1.259417961078379,7.934560825915539,30.0437,22403,timeout +9,3,30.0422,0.6804485880286448,7.934560825915539,30.066,22202,timeout +10,3,30.0422,0.3434968599253663,7.934560825915539,30.0449,21973,timeout +11,3,30.0422,1.0905404782748929,7.934560825915539,30.0527,21702,timeout +12,8,30.0422,22.049810202759005,22.049810202759005,30.1074,21601,timeout +13,8,30.0422,0.17847965238517965,22.049810202759005,30.0533,21402,timeout +14,8,30.0422,0.27118803942838277,22.049810202759005,30.0459,21202,timeout +15,8,30.0422,0.20838358014567557,22.049810202759005,30.0479,20730,timeout +16,8,30.0422,0.49252511656972553,22.049810202759005,30.0524,20802,timeout +17,8,30.0422,0.44638012280451234,22.049810202759005,30.2257,7002,timeout +18,8,30.0422,0.22255965292841648,22.049810202759005,30.2235,4609,timeout +19,8,30.0422,1.8237068965517242,22.049810202759005,30.1937,4639,timeout +20,8,30.0422,1.885182584269663,22.049810202759005,30.1548,5695,timeout +21,8,30.0422,0.234605478810583,22.049810202759005,959.4193,4270,timeout +22,8,30.0422,0.4667535853976532,22.049810202759005,30.5273,4601,timeout +23,8,30.0422,0.9709768022469573,22.049810202759005,30.2174,9612,timeout +24,8,30.0422,0.13200306983883345,22.049810202759005,1741.3819,1302,timeout +25,8,30.0422,0.2905625290562529,22.049810202759005,30.6535,4301,timeout +26,8,30.0422,0.5323171335246957,22.049810202759005,30.228,10102,timeout +27,8,30.0422,0.19258202567760344,22.049810202759005,1073.5067,1401,timeout +28,8,30.0422,0.37563923756392376,22.049810202759005,30.6017,4301,timeout +29,8,30.0422,0.46404518792092114,22.049810202759005,30.2799,4602,timeout +30,8,30.0422,0.0768277571251549,22.049810202759005,940.3711,806,timeout +31,8,30.0422,0.14260328742780987,22.049810202759005,4638.4381,4501,timeout +32,8,30.0422,2.2750164221589664,22.049810202759005,30.1912,4566,timeout +33,8,30.0422,0.14547001276052743,22.049810202759005,2521.3001,2350,timeout +34,8,30.0422,1.502520271751041,22.049810202759005,30.1943,4562,timeout +35,8,30.0422,7.772192391180167,22.049810202759005,30.193,8752,timeout +36,8,30.0422,1.7314197847257817,22.049810202759005,30.4923,3901,timeout +37,8,30.0422,0.9427839453458582,22.049810202759005,30.1916,4683,timeout +38,8,30.0422,7.496638197346901,22.049810202759005,30.3508,5502,timeout +39,8,30.0422,0.37126715092816787,22.049810202759005,30.1195,6194,timeout +40,8,30.0422,0.944994361608821,22.049810202759005,30.1912,7980,timeout +41,8,30.0422,11.610241820768136,22.049810202759005,1048.2961,702,timeout +42,8,30.0422,0.11639271434917814,22.049810202759005,30.5286,4501,timeout +43,8,30.0422,0.9478374266463813,22.049810202759005,30.6514,9201,timeout +44,8,30.0422,0.12410841654778887,22.049810202759005,962.3904,1401,timeout +45,8,30.0422,0.24451977401129943,22.049810202759005,30.2095,4424,timeout +46,8,30.0422,0.44569246321464673,22.049810202759005,30.4792,8902,timeout +47,8,30.0422,0.21783207694307252,22.049810202759005,30.2263,3846,timeout +48,8,30.0422,0.19107969778211065,22.049810202759005,30.4282,4102,timeout +49,8,30.0422,0.8888450148075024,22.049810202759005,30.1557,5064,timeout +50,8,30.0422,0.5368871965730605,22.049810202759005,928.3899,4201,timeout +51,8,30.0422,3.6201766620176663,22.049810202759005,30.5462,4301,timeout +52,8,30.0422,0.8182016111473982,22.049810202759005,30.1921,9185,timeout +53,8,30.0422,20.374090247452692,22.049810202759005,1745.3179,1373,timeout +54,8,30.0422,1.882326215356656,22.049810202759005,30.5762,4401,timeout +55,8,30.0422,0.09853638425066996,22.049810202759005,30.6706,9701,timeout +56,8,30.0422,0.07315045719035744,22.049810202759005,1051.3446,1202,timeout +57,8,30.0422,0.20697728869117302,22.049810202759005,30.2021,4270,timeout +58,8,30.0422,0.9230273059557085,22.049810202759005,30.4669,9301,timeout +59,8,30.0422,5.656152820968458,22.049810202759005,30.6189,4501,timeout +60,8,30.0422,0.28964904486894716,22.049810202759005,30.5778,4501,timeout +61,8,30.0422,1.4663398076560437,22.049810202759005,30.1929,5302,timeout +62,8,30.0422,1.6571345623457687,22.049810202759005,30.1186,6888,timeout +63,8,30.0422,0.5305612360827084,22.049810202759005,30.2284,8801,timeout +64,8,30.0422,1.3093693502073807,22.049810202759005,30.1139,9402,timeout +65,8,30.0422,1.473933139847361,22.049810202759005,30.2209,9302,timeout +66,8,30.0422,5.168186312204767,22.049810202759005,30.1385,9102,timeout +67,8,30.0422,1.1721004203112575,22.049810202759005,30.2265,8802,timeout +68,8,30.0422,0.8765193683971373,22.049810202759005,30.1406,8802,timeout +69,8,30.0422,0.21329311644942367,22.049810202759005,30.1065,9282,timeout +70,8,30.0422,0.44294718539447947,22.049810202759005,30.352,9201,timeout +71,8,30.0422,0.11744426568604918,22.049810202759005,30.2646,8701,timeout +72,8,30.0422,9.694091903719912,22.049810202759005,30.115,9139,timeout +73,8,30.0422,0.11952899355698733,22.049810202759005,30.2496,9001,timeout +74,8,30.0422,0.11440630900810841,22.049810202759005,30.1412,9002,timeout +75,8,30.0422,0.7825885084155543,22.049810202759005,30.1203,8614,timeout +76,8,30.0422,1.6442687747035574,22.049810202759005,30.2894,8601,timeout +77,8,30.0422,0.35340895801660627,22.049810202759005,30.1207,8550,timeout +78,8,30.0422,0.08320459059810197,22.049810202759005,30.1153,9061,timeout +79,13,30.0422,69.5112740197712,69.5112740197712,30.1846,9002,timeout +80,13,30.0422,0.16951788491446346,69.5112740197712,30.2959,9001,timeout +81,13,30.0422,56.3444008092348,69.5112740197712,30.1492,8402,timeout +82,13,30.0422,2.356469385436783,69.5112740197712,30.1864,8802,timeout +83,13,30.0422,4.475544448411282,69.5112740197712,30.1953,8402,timeout +84,13,30.0422,0.07702794819359236,69.5112740197712,30.2692,8801,timeout +85,13,30.0422,0.4021813224267212,69.5112740197712,30.319,8801,timeout +86,13,30.0422,5.324940047961631,69.5112740197712,30.1302,8339,timeout +87,13,30.0422,10.743155742360559,69.5112740197712,30.2093,8802,timeout +88,13,30.0422,1.1188232590209148,69.5112740197712,30.2905,8701,timeout +89,13,30.0422,1.4401930368838332,69.5112740197712,30.1912,8702,timeout +90,13,30.0422,0.2368483694358486,69.5112740197712,30.2907,8401,timeout +91,13,30.0422,4.45356271068232,69.5112740197712,30.1715,8602,timeout +92,13,30.0422,0.10778859527121001,69.5112740197712,30.1187,8627,timeout +93,13,30.0422,0.34096162736939434,69.5112740197712,30.1169,8651,timeout +94,13,30.0422,0.34708822503777753,69.5112740197712,30.2075,8602,timeout +95,13,30.0422,0.19923827660080934,69.5112740197712,30.3524,8401,timeout +96,13,30.0422,0.0571712652249565,69.5112740197712,30.1293,8045,timeout +97,29,30.0422,159.74417788180222,159.74417788180222,30.1213,8544,timeout +98,29,30.0422,0.6839315827511443,159.74417788180222,30.2607,8301,timeout +99,29,30.0422,0.783804048987753,159.74417788180222,30.2624,8001,timeout +100,29,30.0422,12.445235552391123,159.74417788180222,30.1211,8426,timeout +101,29,30.0422,0.4217335058214748,159.74417788180222,30.2357,8502,timeout +102,29,30.0422,0.5359990479590623,159.74417788180222,30.2436,8402,timeout +103,29,30.0422,0.32086881401949896,159.74417788180222,30.2405,8102,timeout +104,29,30.0422,0.2178919397697077,159.74417788180222,30.2454,7902,timeout +105,29,30.0422,0.11003236245954692,159.74417788180222,30.1227,8342,timeout +106,29,30.0422,2.1505674653215636,159.74417788180222,30.144,7929,timeout +107,29,30.0422,3.3136990002701974,159.74417788180222,30.3938,7401,timeout +108,29,30.0422,0.41915202321457357,159.74417788180222,30.3389,6202,timeout +109,29,30.0422,0.2530210353573027,159.74417788180222,30.3066,6702,timeout +110,29,30.0422,0.8468912626677623,159.74417788180222,30.4052,7301,timeout +111,29,30.0422,0.10021945866861741,159.74417788180222,30.3321,8201,timeout +112,29,30.0422,1.2965988053151287,159.74417788180222,30.15,8202,timeout +113,29,30.0422,0.2016949152542373,159.74417788180222,30.1256,8259,timeout +114,29,30.0422,0.5469920270596762,159.74417788180222,30.1211,8277,timeout +115,29,30.0422,0.3803665256952454,159.74417788180222,30.187,7802,timeout +116,29,30.0422,0.12595892105914377,159.74417788180222,30.1219,8081,timeout +117,29,30.0422,0.7335640138408305,159.74417788180222,30.1866,7802,timeout +118,29,30.0422,0.15482140680238937,159.74417788180222,30.1567,8202,timeout +119,29,30.0422,0.18655992236778263,159.74417788180222,30.1248,8243,timeout +120,29,30.0422,34.76696238910409,159.74417788180222,30.2552,8002,timeout +121,29,30.0422,0.2664941785252264,159.74417788180222,30.1762,8502,timeout +122,29,30.0422,0.1139484461575524,159.74417788180222,30.3577,8301,timeout +123,29,30.0422,2.567885613058332,159.74417788180222,30.1731,7902,timeout +124,29,30.0422,27.49474493719559,159.74417788180222,30.2753,7801,timeout +125,29,30.0422,4.03192007797271,159.74417788180222,30.127,8207,timeout +126,29,30.0422,38.59544573643411,159.74417788180222,30.1522,8255,timeout +127,29,30.0422,0.473920287069076,159.74417788180222,30.2138,7802,timeout +128,29,30.0422,39.834694623918075,159.74417788180222,30.2391,8202,timeout +129,29,30.0422,17.543825429720833,159.74417788180222,30.1499,8202,timeout +130,29,30.0422,0.38318551816304214,159.74417788180222,30.1297,8230,timeout +131,29,30.0422,4.840182648401827,159.74417788180222,30.2512,8102,timeout +132,29,30.0422,5.426976502661301,159.74417788180222,30.187,7702,timeout +133,29,30.0422,0.36902160718621024,159.74417788180222,30.1266,8237,timeout +134,29,30.0422,0.3211767566253343,159.74417788180222,30.1337,8225,timeout +135,29,30.0422,1.3602170427919595,159.74417788180222,30.1371,8108,timeout +136,29,30.0422,25.04127146885414,159.74417788180222,30.3226,7801,timeout +137,29,30.0422,0.11849323418261612,159.74417788180222,30.2326,8202,timeout +138,29,30.0422,0.4214876033057851,159.74417788180222,30.1164,7985,timeout +139,29,30.0422,6.744329104190696,159.74417788180222,30.1951,7802,timeout +140,29,30.0422,0.2928493117310269,159.74417788180222,30.1273,8208,timeout +141,29,30.0422,4.793338195964017,159.74417788180222,30.1427,8225,timeout +142,29,30.0422,0.2912271674813481,159.74417788180222,30.1214,7773,timeout +143,29,30.0422,0.10144927536231885,159.74417788180222,30.1169,8279,timeout +144,29,30.0422,0.3049731838127743,159.74417788180222,30.1258,8203,timeout +145,29,30.0422,1.6470301850048685,159.74417788180222,30.1283,8215,timeout +146,29,30.0422,1.0892276930767308,159.74417788180222,30.2824,8001,timeout +147,29,30.0422,7.4027202838557065,159.74417788180222,642.6021,3381,timeout diff --git a/polymorphic_solver/src/morphemic.py b/polymorphic_solver/src/morphemic.py new file mode 100644 index 0000000000000000000000000000000000000000..69fc8ea2b2ac411ae7d7bf06e8a96d8a5982b751 --- /dev/null +++ b/polymorphic_solver/src/morphemic.py @@ -0,0 +1,181 @@ +import os, time +from itertools import combinations + +default_camel_folder = os.environ.get("CAMEL_FOLDER","/tmp/camel") + +class MorphemicArchetype(): + def __init__(self, component_name, index, variant, hw): + self.variant = variant + self.hw = hw + self.component_name = component_name + self.component_index = index + self.index_state = None + self.key = '{0}{1}{2}'.format(self.component_index, self.variant, self.hw) + self.collections = [] + self.lowest = [None, None, None, None] + self.highest = [None, None, None, None] + print('Archetype for component index = {0} Variant = {1}, HW= {2} for component {3} created'.format(self.component_index, self.variant, self.hw, self.component_name)) + + def addLine(self, mem, cpu, instance, performance): + self.collections.append([mem, cpu, instance, performance]) + def getIndex(self): + return self.component_index + def getKey(self): + return self.key + def getComponentName(self): + return self.component_name + + def clean(self): + del self.collections[:] + self.lowest = [None, None, None, None] + self.highest = [None, None, None, None] + + def getVariant(self): + return self.variant + def getIndexState(self): + return self.index_state + def setIndexState(self, index_state): + self.index_state = index_state + def computeLowestHighest(self): + for collection in self.collections: + for i in range(len(self.lowest)): + if self.lowest[i] == None or collection[i] < self.lowest[i]: + self.lowest[i] = collection[i] + if self.highest[i] == None or collection[i] > self.highest[i]: + self.highest[i] = collection[i] + def getLowestHighest(self): + return self.lowest, self.highest + + def getHW(self): + return self.hw + def __repr__(self): + return 'Archetype Index = {0} Variant = {1}, HW= {2}, Lowest = {3}, Highest = {4} for component {5}'.format(self.component_index,self.variant, self.hw, self.lowest, self.highest, self.component_name) + +class MorphemicArchetypeManager(): + def __init__(self): + self.archetypes = [] + self.index_state = 0 + self.number_of_components = None + self.camel_transformer = CamelTransformer() + + def setNumberOfComponents(self, number): + self.number_of_components = number + + def getNumberOfComponents(self): + return self.number_of_components + + def cleanAllArchetypes(self): + for archetype in self.archetypes: + archetype.clean() + + def createArchetypes(self, component_name, index, variants, hws): + for variant in variants: + for hw in hws: + if hw == "FPGA" and variant == "SERVERLESS": + continue + archetype = MorphemicArchetype(component_name,index, variant, hw) + self.archetypes.append(archetype) + + def getArchetype(self, index, variant, hw): + for archetype in self.archetypes: + if archetype.getIndex() == index and archetype.getVariant() == variant and archetype.getHW() == hw: + return archetype + return None + + def getArchetypeByKey(self, key): + for archetype in self.archetypes: + if archetype.getKey() == key: + return archetype + return None + + def addLine(self, states, performance): + for i in range(len(states)): + state = states[i] + hw = state.properties[3] + variant = state.properties[4] + archetype = self.getArchetype(i, variant, hw) + if archetype: + archetype.setIndexState(self.index_state) + archetype.addLine(state.properties[0], state.properties[1], state.properties[2], performance) + self.index_state +=1 + + def isGroupIsCompleted(self, group): + index_archetypes = [] + for key in group: + archetype = self.getArchetypeByKey(key) + index_archetypes.append(archetype.getIndex()) + for i in range(self.number_of_components): + if not i in index_archetypes: + return False + return True + + def getArchetypesByGroup(self, group): + result = [] + for key in group: + archetype = self.getArchetypeByKey(key) + if archetype: + result.append(archetype) + return result + + def analyse(self): + all_keys = [] + for archetype in self.archetypes: + archetype.computeLowestHighest() + lowest, highest = archetype.getLowestHighest() + if all(lowest) and all(highest): + all_keys.append(archetype.getKey()) + + if len(all_keys) >= 2: + index = 0 + for group in combinations(all_keys, self.number_of_components): + if self.isGroupIsCompleted(group): + archetypes = self.getArchetypesByGroup(group) + index +=1 + self.camel_transformer.archetypesToCamels(archetypes) + print(":::: Report ::::") + print("Archetypes size = {0}".format(index)) + self.cleanAllArchetypes() + + +class CamelTransformer(): + def __init__(self): + self.folder = default_camel_folder + self.filename = None + + def setFilename(self, name): + self.filename = name + + def camelToPolymorphicSolverJSON(self): + result = {} + return result + + def loadCamel(self): + if self.filename: + pass + + def archetypesToCamels(self, archetypes): + max_performance = None + if archetypes[0].highest[3] > 100: + max_performance = 100 + else: + max_performance = archetypes[0].highest[3] + data = {'components':[], 'min_performance': archetypes[0].lowest[3], 'max_performance': max_performance} + for archetype in archetypes: + comp_data = {} + comp_data['name'] = archetype.getComponentName() + comp_data['variant'] = archetype.getVariant() + comp_data['hw'] = archetype.getHW() + comp_data['min_mem'] = archetype.lowest[0] + comp_data['min_cpu'] = archetype.lowest[1] + comp_data['max_mem'] = archetype.highest[0] + comp_data['max_cpu'] = archetype.highest[1] + data['components'].append(comp_data) + print(data) + + + + + + + + diff --git a/polymorphic_solver/src/multiagent-particle-envs b/polymorphic_solver/src/multiagent-particle-envs new file mode 160000 index 0000000000000000000000000000000000000000..5405cc26f6d5698e224f8029351280b1d388ac0b --- /dev/null +++ b/polymorphic_solver/src/multiagent-particle-envs @@ -0,0 +1 @@ +Subproject commit 5405cc26f6d5698e224f8029351280b1d388ac0b diff --git a/polymorphic_solver/src/networks.py b/polymorphic_solver/src/networks.py new file mode 100644 index 0000000000000000000000000000000000000000..92dc76416dbbc2c5181d917345b9f3640bf88cf6 --- /dev/null +++ b/polymorphic_solver/src/networks.py @@ -0,0 +1,68 @@ +import os +from os import path +import torch as T +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim + +class CriticNetwork(nn.Module): + def __init__(self, beta, input_dims, fc1_dims, fc2_dims, + n_agents, n_actions, name, chkpt_dir): + super(CriticNetwork, self).__init__() + + self.chkpt_file = os.path.join(chkpt_dir, name) + + self.fc1 = nn.Linear(input_dims+n_agents*n_actions, fc1_dims) + self.fc2 = nn.Linear(fc1_dims, fc2_dims) + self.q = nn.Linear(fc2_dims, 1) + + self.optimizer = optim.Adam(self.parameters(), lr=beta) + self.device = T.device('cuda:0' if T.cuda.is_available() else 'cpu') + + self.to(self.device) + + def forward(self, state, action): + x = F.relu(self.fc1(T.cat([state, action], dim=1))) + x = F.relu(self.fc2(x)) + q = self.q(x) + + return q + + def save_checkpoint(self): + T.save(self.state_dict(), self.chkpt_file) + + def load_checkpoint(self): + if path.exists(self.chkpt_file+"/agent_0_critic"): + self.load_state_dict(T.load(self.chkpt_file)) + + +class ActorNetwork(nn.Module): + def __init__(self, alpha, input_dims, fc1_dims, fc2_dims, + n_actions, name, chkpt_dir): + super(ActorNetwork, self).__init__() + + self.chkpt_file = os.path.join(chkpt_dir, name) + + self.fc1 = nn.Linear(input_dims, fc1_dims) + self.fc2 = nn.Linear(fc1_dims, fc2_dims) + self.pi = nn.Linear(fc2_dims, n_actions) + + self.optimizer = optim.Adam(self.parameters(), lr=alpha) + self.device = T.device('cuda:0' if T.cuda.is_available() else 'cpu') + + self.to(self.device) + + def forward(self, state): + x = F.relu(self.fc1(state)) + x = F.relu(self.fc2(x)) + pi = T.softmax(self.pi(x), dim=1) + + return pi + + def save_checkpoint(self): + T.save(self.state_dict(), self.chkpt_file) + + def load_checkpoint(self): + if path.exists(self.chkpt_file+"/agent_0_critic"): + self.load_state_dict(T.load(self.chkpt_file)) + diff --git a/polymorphic_solver/src/request_sender.py b/polymorphic_solver/src/request_sender.py new file mode 100644 index 0000000000000000000000000000000000000000..a81974c81b68d0c6513b81bf5c40dd33cfd2e723 --- /dev/null +++ b/polymorphic_solver/src/request_sender.py @@ -0,0 +1,13 @@ +import json, requests + +url = "http://localhost:7879/groups" + +data = { + "utility": "0.7", + "json_path": "./json/configs.json", + "result":[["Component1","Component2"]] +} + +response = requests.post(url, json.dumps(data)).text +print(response) + diff --git a/polymorphic_solver/src/resource_logic.py b/polymorphic_solver/src/resource_logic.py new file mode 100644 index 0000000000000000000000000000000000000000..07d2a4cbf2917e6549b047c7b08bcccd86d9d5c0 --- /dev/null +++ b/polymorphic_solver/src/resource_logic.py @@ -0,0 +1,217 @@ +import os, math + +class ResourceLogic(): + def __init__(self, min_mem, max_mem, min_cpu, max_cpu, max_instances): + self.mem_list = [] + self.cpu_list = [] + self.instance_list = [] + self.min_mem = min_mem + self.max_mem = max_mem + self.min_cpu = min_cpu + self.max_cpu = max_cpu + self.max_instances = max_instances + self.prepareList() + + def getMemSize(self, current, action): + index = self.mem_list.index(current) + if action == "SCALE_MEM_UP": + if index >= len(self.mem_list) - 1: + return None + else: + return self.mem_list[index+1] + if action == "SCALE_MEM_DOWN": + if index == 0: + return None + else: + return self.mem_list[index-1] + if action == "DOUBLE_MEM": + if index >= len(self.mem_list) - 2: + return None + else: + return self.mem_list[index+2] + if action == "DEVIDE_MEM": + if index < 2: + return None + else: + return self.mem_list[index-2] + + def getNumberCPU(self, current, action): + index = self.cpu_list.index(current) + if action == "SCALE_CPU_UP": + if index >= len(self.cpu_list) - 1: + return None + else: + return self.cpu_list[index + 1] + if action == "SCALE_CPU_DOWN": + if index < 2: + return None + else: + return self.cpu_list[index - 1] + if action == "DOUBLE_CPU": + double_cpu_value = current * 2 + if double_cpu_value in self.cpu_list: + return double_cpu_value + return None + if action == "DEVIDE_CPU": + devided_cpu_value = current // 2 + if devided_cpu_value in self.cpu_list and devided_cpu_value != 0: + return devided_cpu_value + else: + return None + + def getNumberInstances(self, current, action): + index = self.instance_list.index(current) + if action == "SCALE_INSTANCE_UP": + if index == len(self.instance_list) - 1: + return self.max_instances + else: + return self.instance_list[index + 1] + if action == "SCALE_INSTANCE_DOWN": + if index == 0: + return 1 + else: + return self.instance_list[index-1] + if action == "DOUBLE_INSTANCES": + new_n_instances = current * 2 + if new_n_instances in self.instance_list: + return new_n_instances + else: + return self.max_instances + if action == "DEVIDE_INSTANCES": + new_n_instances = current // 2 + if new_n_instances in self.instance_list: + return new_n_instances + else: + return 1 + + def getMemCPU(self, mem, cpu, action): + index_mem = self.mem_list.index(mem) + index_cpu = self.cpu_list.index(cpu) + if action == "MEM_UP_CPU_UP": + if index_mem >= len(self.mem_list) - 1 or index_cpu >= len(self.cpu_list) - 1: + return False, None, None + else: + mem = self.mem_list[index_mem + 1] + cpu = self.cpu_list[index_cpu + 1] + return True, mem, cpu + + if action == "MEM_UP_CPU_DOWN": + if index_mem >= len(self.mem_list) - 1 or index_cpu < 2: + return False, None, None + else: + mem = self.mem_list[index_mem + 1] + cpu = self.cpu_list[index_cpu - 1] + return True, mem, cpu + if action == "MEM_DOWN_CPU_UP": + if index_mem == 0 or index_cpu >= len(self.cpu_list) - 1: + return False, None, None + else: + mem = self.mem_list[index_mem - 1] + cpu = self.cpu_list[index_cpu + 1] + return True, mem, cpu + if action == "MEM_DOWN_CPU_DOWN": + if index_mem == 0 or index_cpu < 2: + return False, None, None + else: + mem = self.mem_list[index_mem - 1] + cpu = self.cpu_list[index_cpu - 1] + return True, mem, cpu + + def getMemCpuInstances(self, mem, cpu, instances, action): + index_mem = self.mem_list.index(mem) + index_cpu = self.cpu_list.index(cpu) + index_ins = self.instance_list.index(instances) + if action == "MEM_UP_CPU_UP_INSTANCE_UP": + if index_mem >= len(self.mem_list) - 1 or index_cpu >= len(self.cpu_list) - 1 or index_ins >= len(self.instance_list) - 1: + return False, None, None, None + else: + mem = self.mem_list[index_mem + 1] + cpu = self.cpu_list[index_cpu + 1] + instances = self.instance_list[index_ins + 1] + return True, mem, cpu, instances + + if action == "MEM_UP_CPU_UP_INSTANCE_DOWN": + if index_mem >= len(self.mem_list) - 1 or index_cpu >= len(self.cpu_list) - 1 or index_ins == 0: + return False, None, None, None + else: + mem = self.mem_list[index_mem + 1] + cpu = self.cpu_list[index_cpu + 1] + instances = self.instance_list[index_ins - 1] + return True, mem, cpu, instances + + if action == "MEM_UP_CPU_DOWN_INSTANCE_UP": + if index_mem >= len(self.mem_list) - 1 or index_cpu < 2 or index_ins >= len(self.instance_list) -1: + return False, None, None, None + else: + mem = self.mem_list[index_mem + 1] + cpu = self.cpu_list[index_cpu - 1] + instances = self.instance_list[index_ins + 1] + return True, mem, cpu, instances + if action == "MEM_UP_CPU_DOWN_INSTANCE_DOWN": + if index_mem >= len(self.mem_list) - 1 or index_cpu < 2 or index_ins == 0: + return False, None, None, None + else: + mem = self.mem_list[index_mem + 1] + cpu = self.cpu_list[index_cpu - 1] + instances = self.instance_list[index_ins - 1] + return True, mem, cpu, instances + if action == "MEM_DOWN_CPU_UP_INSTANCE_UP": + if index_mem == 0 or index_cpu >= len(self.cpu_list) - 1 or index_ins >= len(self.instance_list) - 1: + return False, None, None, None + else: + mem = self.mem_list[index_mem - 1] + cpu = self.cpu_list[index_cpu + 1] + instances = self.instance_list[index_ins + 1] + return True, mem, cpu, instances + if action == "MEM_DOWN_CPU_UP_INSTANCE_DOWN": + if index_mem == 0 or index_cpu >= len(self.cpu_list) - 1 or index_ins == 0: + return False, None, None, None + else: + mem = self.mem_list[index_mem - 1] + cpu = self.cpu_list[index_cpu + 1] + instances = self.instance_list[index_ins - 1] + return True, mem, cpu, instances + if action == "MEM_DOWN_CPU_DOWN_INSTANCE_UP": + if index_mem == 0 or index_cpu < 2 or index_ins >= len(self.instance_list) - 1: + return False, None, None, None + else: + mem = self.mem_list[index_mem - 1] + cpu = self.cpu_list[index_cpu - 1] + instances = self.instance_list[index_ins + 1] + return True, mem, cpu, instances + + if action == "MEM_DOWN_CPU_DOWN_INSTANCE_DOWN": + if index_mem == 0 or index_cpu < 2 or index_ins == 0: + return False, None, None, None + else: + mem = self.mem_list[index_mem - 1] + cpu = self.cpu_list[index_cpu - 1] + instances = self.instance_list[index_ins - 1] + return True, mem, cpu, instances + + def getMemList(self): + return self.mem_list + + def getCPUList(self): + return self.cpu_list + + def getNumberInstancesList(self): + return self.instance_list + + + def prepareList(self): + self.instance_list = list(range(1, self.max_instances + 1, 1)) + self.cpu_list = list(range(self.min_cpu, self.max_cpu + 1, 1)) + if not 0 in self.cpu_list: + self.cpu_list.insert(0,0) #insert 0 in the first position + index = 0 + while True: + mem = int(math.pow(2, index)) + if mem < self.min_mem: + index +=1 + continue + if mem > self.max_mem: + break + self.mem_list.append(mem) + index +=1 + diff --git a/polymorphic_solver/src/start_api.sh b/polymorphic_solver/src/start_api.sh new file mode 100755 index 0000000000000000000000000000000000000000..a3095321e971e80e155be6cf144f8de426f2a720 --- /dev/null +++ b/polymorphic_solver/src/start_api.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/apivenv/bin/python -u api.py diff --git a/polymorphic_solver/src/start_solver.sh b/polymorphic_solver/src/start_solver.sh new file mode 100755 index 0000000000000000000000000000000000000000..9b2dea13e14d891815aae7ba5c7fc98719b5e4ff --- /dev/null +++ b/polymorphic_solver/src/start_solver.sh @@ -0,0 +1,2 @@ +#!/bin/bash +python -u app.py diff --git a/polymorphic_solver/src/supervisord.conf b/polymorphic_solver/src/supervisord.conf new file mode 100644 index 0000000000000000000000000000000000000000..ad072032848139a9c6bb7ab50763484ece88157a --- /dev/null +++ b/polymorphic_solver/src/supervisord.conf @@ -0,0 +1,32 @@ +[unix_http_server] +file=/run/supervisor.sock +chmod=0770 + +[supervisord] +nodaemon=true +pidfile=/run/pid/supervisord.pid +logfile=/var/log/supervisor/supervisord.log +childlogdir=/var/log/supervisor +logfile_maxbytes=50MB +logfile_backups=1 + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///run/supervisor.sock + + +[program:solver] +command=/app/start_solver.sh +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:api] +command=/app/start_api.sh +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/polymorphic_solver/src/tmp/.DS_Store b/polymorphic_solver/src/tmp/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c2285676f2b54d79d1cddcf755b82e6f0d85e15c Binary files /dev/null and b/polymorphic_solver/src/tmp/.DS_Store differ diff --git a/polymorphic_solver/src/tmp/maddpg/.DS_Store b/polymorphic_solver/src/tmp/maddpg/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..05f4bb1ffdb515968b252b9ffef83cf4a3cc0b20 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/.DS_Store differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/.DS_Store b/polymorphic_solver/src/tmp/maddpg/default_application/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..059d3ec90731d6a20e69852298a22a868f5b11e3 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/.DS_Store differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_actor b/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_actor new file mode 100644 index 0000000000000000000000000000000000000000..507b59a982d9756a28467ccce85c4aafd1970314 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_critic b/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_critic new file mode 100644 index 0000000000000000000000000000000000000000..b47f4b41be5f29b39ef42bb59e716ab61324e41e Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_target_actor b/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_target_actor new file mode 100644 index 0000000000000000000000000000000000000000..641577ad798164b0b4aa90b574f0d952987f270e Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_target_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_target_critic b/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_target_critic new file mode 100644 index 0000000000000000000000000000000000000000..2cbd251be6620bead719ab3a7a5b10b1d3ed7021 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/agent_0_target_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_actor b/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_actor new file mode 100644 index 0000000000000000000000000000000000000000..b6e2f3578a55de093f592c29806095adc6186714 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_critic b/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_critic new file mode 100644 index 0000000000000000000000000000000000000000..d083baebc12c2b49ab91504e04c5e6e8bdc3066a Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_target_actor b/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_target_actor new file mode 100644 index 0000000000000000000000000000000000000000..9ad9e122c58ece4a6c3927f0e072d7f252e36b76 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_target_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_target_critic b/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_target_critic new file mode 100644 index 0000000000000000000000000000000000000000..a8957c1fa52aa8972e03d3fc7e26b47af6f6f4f0 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/default_application/agent_1_target_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/.DS_Store b/polymorphic_solver/src/tmp/maddpg/simple_adversary/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/.DS_Store differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_actor b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_actor new file mode 100644 index 0000000000000000000000000000000000000000..8d6e25e97e02f0200386e98649249d5cc3a13e80 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_critic b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_critic new file mode 100644 index 0000000000000000000000000000000000000000..0bce24a282e096e9cc155fdda9c87bcf72f0b07a Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_target_actor b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_target_actor new file mode 100644 index 0000000000000000000000000000000000000000..8e1bad6199406215b0cb54c63ab762513b796f32 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_target_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_target_critic b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_target_critic new file mode 100644 index 0000000000000000000000000000000000000000..cfa85147bf3f2559bd7fc652a64daae2eaa8f43d Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_0_target_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_actor b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_actor new file mode 100644 index 0000000000000000000000000000000000000000..45435612e1beb6622dd38a0c4507eaf5e7c4fce6 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_critic b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_critic new file mode 100644 index 0000000000000000000000000000000000000000..fa7dbd7861cc114f8cbfab5251583070d2881b23 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_target_actor b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_target_actor new file mode 100644 index 0000000000000000000000000000000000000000..1c1b9f9620211d92339877c2512cf055ac8ae57e Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_target_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_target_critic b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_target_critic new file mode 100644 index 0000000000000000000000000000000000000000..c1df391d1f3856e0bdfa18d6df627829b6bde304 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_1_target_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_actor b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_actor new file mode 100644 index 0000000000000000000000000000000000000000..be181b83b68d6ebedd2212b4e467faa05ca18908 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_critic b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_critic new file mode 100644 index 0000000000000000000000000000000000000000..7a93cc2cc877f52550340ef25990e07344143457 Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_critic differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_target_actor b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_target_actor new file mode 100644 index 0000000000000000000000000000000000000000..1c9b055ff84b1a2bbd5cdf3eee22c6b84824a06f Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_target_actor differ diff --git a/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_target_critic b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_target_critic new file mode 100644 index 0000000000000000000000000000000000000000..4e1ed4f4d902f7b60aafa45d2ce378a42d09161d Binary files /dev/null and b/polymorphic_solver/src/tmp/maddpg/simple_adversary/agent_2_target_critic differ