Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedded python #395

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
compile_commands.json
*.plan
results
.oppfeaturestate

### coverage files
quisp/coverage*
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "quispr"]
path = quispr
url = https://github.com/sfc-aqua/quispr
[submodule "pybind11"]
path = pybind11
url = https://github.com/pybind/pybind11
Empty file added .nedexclusions
Empty file.
12 changes: 12 additions & 0 deletions .oppfeatures
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<features cppSourceRoots = "quisp" definesFile = "quisp/features.h">
<feature
id = "EmbeddedPython"
name = "Embedded Python"
description = "this allows user to run python code in a simulation"
initiallyEnabled = "false"
extraSourceFolders = ""
compileFlags = "-DENABLE_PYTHON"
linkerFlags = ""
/>

</features>
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
QUISP_MAKEFILE = "./quisp/Makefile"
QUISP_SRC_DIR = "./quisp"
QUISP_MAKEFILE = "$(QUISP_SRC_DIR)/Makefile"
QUISP_FEATURE = "$(QUISP_SRC_DIR)/feature_defines.h"

.PHONY: all tidy format ci makefile-exe makefile-lib checkmakefile googletest clean test coverage coverage-report help quispr

Expand Down Expand Up @@ -47,12 +49,15 @@ eigen/CMakeLists.txt:

eigen: eigen/CMakeLists.txt

makefile-exe: eigen
makefile-exe: eigen $(QUISP_FEATURE)
cd quisp && opp_makemake -f --deep -O out -i ./makefrag

makefile-lib: eigen
makefile-lib: eigen $(QUISP_FEATURE)
cd quisp && opp_makemake -f --deep -O out -i ./makefrag -M debug --make-so

$(QUISP_FEATURE): $(wildcard .oppfeaturestate) .oppfeatures
opp_featuretool defines > $(QUISP_SRC_DIR)/feature_defines.h

clean:
@if [ -f "$(QUISP_MAKEFILE)" ]; then \
$(MAKE) -C quisp clean; \
Expand Down
1 change: 1 addition & 0 deletions pybind11
Submodule pybind11 added at ffa346
1 change: 1 addition & 0 deletions quisp/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ out/
.oppfeaturestate
quisp.dSYM/
Test*
feature_defines.h

PhotonicQubit_m.cc
PhotonicQubit_m.h
Expand Down
6 changes: 3 additions & 3 deletions quisp/.nedfolders
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.
./channels
./modules
./networks
channels
modules
networks
16 changes: 16 additions & 0 deletions quisp/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import opp
import application

def init_app_module(app):
num_resources = app.par("distant_measure_count").int()
pattern = app.par("TrafficPattern").int()
initiator_addr = app.par("LoneInitiatorAddress").int()

if pattern == 1 and app.my_address == initiator_addr:
dest_addr = app.get_one_random_endnode_addr()
pk = app.create_conn_setup_req(dest_addr,num_resources)
app.schedule_at(opp.sim_time(), pk)
if pattern == 2:
dest_addr = app.get_one_random_endnode_addr()
pk = app.create_conn_setup_req(dest_addr,num_resources)
app.schedule_at(opp.sim_time() + 0.00001 * app.my_address, pk)
4 changes: 3 additions & 1 deletion quisp/makefrag
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ else
INCLUDE_PATH+=-I. $(shell pkg-config eigen3 --cflags)
endif

INCLUDE_PATH+=-I.
INCLUDE_PATH+=-I$(PROJ_ROOT)/pybind11/include
INCLUDE_PATH+=-I$(OMNETPP_TOOLS_DIR)/lib/Python.framework/Headers
ifneq (,$(ENABLE_COVERAGE))
CXXFLAGS+=-ftest-coverage -fprofile-instr-generate -fcoverage-mapping
LDFLAGS+=-ftest-coverage -fprofile-instr-generate -fcoverage-mapping
endif

# include path for json
INCLUDE_PATH+=-I$(PROJ_ROOT)/json/include/
LDFLAGS+=-L$(OMNETPP_TOOLS_DIR)/lib -lpython3.8

default: eigen all

Expand Down
35 changes: 30 additions & 5 deletions quisp/modules/Application/Application.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,32 @@
* \brief Application
*/
#include "Application.h"
#include <omnetpp.h>
#include <pybind11/detail/common.h>
#include <pybind11/pybind11.h>
#include <functional>
#include <memory>
#include <vector>
#include "messages/base_messages_m.h"
#include "messages/connection_setup_messages_m.h"
#include "modules/Application/IApplication.h"
#include "utils/ComponentProvider.h"

using namespace omnetpp;
using namespace quisp::messages;
namespace py = pybind11;

namespace quisp {
namespace modules {

PYBIND11_EMBEDDED_MODULE(application, m) {
py::class_<Application, std::unique_ptr<Application, pybind11::nodelete>, cSimpleModule> QuispApplication(m, "Application");
QuispApplication.def("create_conn_setup_req", &Application::createConnectionSetupRequest);
QuispApplication.def_readonly("my_address", &Application::my_address);
QuispApplication.def("get_one_random_endnode_addr", &Application::getOneRandomEndNodeAddress);
py::class_<ConnectionSetupRequest, std::unique_ptr<ConnectionSetupRequest, py::nodelete>, omnetpp::cMessage>(m, "ConnectionSetupRequest");
}

Application::Application() : provider(utils::ComponentProvider{this}) {}

/**
Expand All @@ -29,19 +46,26 @@ void Application::initialize() {
return;
}

WATCH_VECTOR(other_end_node_addresses);
storeEndNodeAddresses();

#ifdef ENABLE_PYTHON
const char *app_py_path = par("app_py_path").stringValue();
compile_code(app_py_path);
auto obj = py::cast(this);
execute_python("init_app_module", obj);

#else

my_address = provider.getQNode()->par("address");
is_e2e_connection = par("EndToEndConnection");
num_measure = par("distant_measure_count");

WATCH_VECTOR(other_end_node_addresses);
storeEndNodeAddresses();
traffic_pattern = par("TrafficPattern");

if (!is_e2e_connection) {
return;
}

traffic_pattern = par("TrafficPattern");

if (traffic_pattern == 0) {
EV_INFO << "EndToEndConnection is set true. but no traffic pattern specified; proceeding with no traffic\n";
return;
Expand Down Expand Up @@ -72,6 +96,7 @@ void Application::initialize() {
}

error("Invalid TrafficPattern specified.");
#endif
}

/**
Expand Down
4 changes: 2 additions & 2 deletions quisp/modules/Application/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#ifndef MODULES_APPLICATION_H_
#define MODULES_APPLICATION_H_

#include <modules/Common/PythonEmbeddedable.h>
#include "IApplication.h"
#include "utils/ComponentProvider.h"

Expand All @@ -20,12 +21,11 @@ namespace modules {
*
* \brief Application
*/
class Application : public IApplication {
class Application : public IApplication, python_embeddable::PythonEmbeddable {
public:
Application();
~Application() {}

protected:
int my_address;

std::vector<int> other_end_node_addresses;
Expand Down
1 change: 1 addition & 0 deletions quisp/modules/Application/Application.ned
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ simple Application
{
parameters:
@display("i=block/app");
string app_py_path = default("app");
int address;
volatile double sendIaTime @unit(s) = default(exponential(1s)); // time between generating packets
string Other_endnodes_table = "";
Expand Down
8 changes: 8 additions & 0 deletions quisp/modules/Application/Application_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <gtest/gtest.h>
#include <messages/classical_messages.h>
#include <omnetpp.h>
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <test_utils/TestUtils.h>

namespace {
Expand Down Expand Up @@ -35,6 +37,7 @@ class AppTestTarget : public quisp::modules::Application {
explicit AppTestTarget(TestQNode *parent_qnode) : Application(), toRouterGate(new TestGate(this, "toRouter")) {
this->provider.setStrategy(std::make_unique<Strategy>(parent_qnode));
setComponentType(new TestModuleType("test qnode"));
setParStr(this, "app_py_path", "app");
}
virtual ~AppTestTarget() { EVCB.gateDeleted(toRouterGate); }
std::vector<int> getOtherEndNodeAdresses() { return this->other_end_node_addresses; }
Expand All @@ -44,6 +47,8 @@ class AppTestTarget : public quisp::modules::Application {
};

TEST(AppTest, InitSimple) {
pybind11::scoped_interpreter guard{};

auto *sim = prepareSimulation();
auto *mock_qnode = new TestQNode{123};
auto *app = new AppTestTarget{mock_qnode};
Expand All @@ -61,6 +66,7 @@ TEST(AppTest, InitSimple) {
}

TEST(AppTest, Init_OneConnection_NoSender) {
pybind11::scoped_interpreter guard{};
auto *sim = prepareSimulation();
auto *mock_qnode = new TestQNode{123};
auto *app = new AppTestTarget{mock_qnode};
Expand All @@ -79,6 +85,7 @@ TEST(AppTest, Init_OneConnection_NoSender) {
}

TEST(AppTest, Init_OneConnection_Sender) {
pybind11::scoped_interpreter guard{};
auto *sim = prepareSimulation();
auto *mock_qnode = new TestQNode{123};

Expand Down Expand Up @@ -112,6 +119,7 @@ TEST(AppTest, Init_OneConnection_Sender) {
}

TEST(AppTest, Init_OneConnection_Sender_TrafficPattern2) {
pybind11::scoped_interpreter guard{};
auto *sim = prepareSimulation();
auto *mock_qnode = new TestQNode{123};
auto *mock_qnode2 = new TestQNode{456};
Expand Down
9 changes: 8 additions & 1 deletion quisp/modules/Backend/Backend.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
#include "Backend.h"

namespace quisp::modules::backend {
BackendContainer::BackendContainer() {}

BackendContainer::BackendContainer()
#ifdef ENABLE_PYTHON
: interpreter(pybind11::scoped_interpreter{})
#endif
{
}

BackendContainer::~BackendContainer() {}

} // namespace quisp::modules::backend
13 changes: 12 additions & 1 deletion quisp/modules/Backend/Backend.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma once
#include <modules/common_types.h>
#include <omnetpp.h>
#include <pybind11/embed.h>
#include <memory>
#include "RNG.h"
#include "feature_defines.h"

namespace quisp::modules::backend {
using quisp::modules::common::ErrorTrackingBackend;
Expand All @@ -15,6 +17,10 @@ class BackendContainer : public omnetpp::cSimpleModule {
~BackendContainer();

void initialize() override {
#ifdef ENABLE_PYTHON
python_enabled = true;
#endif
WATCH(python_enabled);
auto backend_type = std::string(par("backendType").stringValue());
if (backend_type == "ErrorTrackingBackend") {
backend = std::make_unique<ErrorTrackingBackend>(std::make_unique<RNG>(this));
Expand All @@ -33,7 +39,12 @@ class BackendContainer : public omnetpp::cSimpleModule {
}

protected:
std::unique_ptr<IQuantumBackend> backend;
void configureErrorTrackingBackend();
std::unique_ptr<IQuantumBackend> backend = nullptr;
bool python_enabled = false;
#ifdef ENABLE_PYTHON
pybind11::scoped_interpreter interpreter;
#endif
};

Define_Module(BackendContainer);
Expand Down
5 changes: 4 additions & 1 deletion quisp/modules/Backend/Backend_test.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "Backend.h"
#include <Python.h>
#include <gtest/gtest.h>
#include <test_utils/TestUtils.h>
#include "modules/common_types.h"
Expand Down Expand Up @@ -29,6 +30,7 @@ TEST(BackendContainer, callInitialize) {
}

TEST(BackendContainer, callInitializeWithInvalidBackend) {
Py_Finalize();
auto *sim = utils::prepareSimulation();
BackendContainer *backend = new BackendContainer();
sim->registerComponent(backend);
Expand All @@ -37,7 +39,8 @@ TEST(BackendContainer, callInitializeWithInvalidBackend) {
EXPECT_THROW(backend->callInitialize(), omnetpp::cRuntimeError);
}

TEST(BackendContainer, getQuantumBackend) {
TEST(BackendConpainer, getQuantumBackend) {
Py_Finalize();
auto *sim = utils::prepareSimulation();
BackendContainer *backend = new BackendContainer();
sim->registerComponent(backend);
Expand Down
50 changes: 50 additions & 0 deletions quisp/modules/Common/PythonEmbeddedable.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "PythonEmbeddedable.h"
#include <omnetpp.h>
#include <pybind11/detail/common.h>
#include <pybind11/embed.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <filesystem>
#include <memory>
#include "feature_defines.h"

namespace py = pybind11;
namespace opp = omnetpp;
namespace fs = std::filesystem;
namespace quisp::modules::python_embeddable {

#ifdef ENABLE_PYTHON
PYBIND11_EMBEDDED_MODULE(opp, m) {
py::class_<opp::cPar, std::unique_ptr<opp::cPar, py::nodelete>> OppPar(m, "Par");
OppPar.def("str", &omnetpp::cPar::str);
OppPar.def("int", &omnetpp::cPar::intValue);

py::class_<opp::cSimpleModule, std::unique_ptr<opp::cSimpleModule, py::nodelete>> OppSimpleModule(m, "SimpleModule");
OppSimpleModule.def("par", py::overload_cast<const char*>(&opp::cSimpleModule::par), py::return_value_policy::reference);
OppSimpleModule.def("schedule_at", &opp::cSimpleModule::scheduleAt);
m.def("sim_time", &opp::simTime, py::return_value_policy::reference);

py::class_<opp::SimTime> OppSimTime(m, "SimTime");
OppSimTime.def(py::self + float());
OppSimTime.def(py::self += float());
OppSimTime.def(float() + py::self);

py::class_<opp::cMessage, std::unique_ptr<opp::cMessage, py::nodelete>> OppCMessage(m, "Message");
}
#endif

void PythonEmbeddable::compile_code(const char* file_name) {
auto* env = omnetpp::getEnvir();
auto* config = env->getConfigEx();
fs::path exe_path(env->getArgVector()[0]);
fs::path conf_path(env->resolveResourcePath(config->getFileName()));
py::module sys = py::module::import("sys");
py::cast<py::list>(sys.attr("path")).append(fs::absolute(exe_path.parent_path()).string());
py::cast<py::list>(sys.attr("path")).append(fs::absolute(conf_path.parent_path()).string());
py::print(sys.attr("path"));
module = pybind11::module_::import(file_name);
}

void PythonEmbeddable::execute_python(const char* func_name, py::object& arg1) { py::object ret = module.attr(func_name)(arg1); }

} // namespace quisp::modules::python_embeddable
Loading