226 changed files with 2060 additions and 16155 deletions
-
1.gitignore
-
30CMakeLists.txt
-
6doc/build.md
-
151resources/3rdparty/CMakeLists.txt
-
25resources/3rdparty/carl/CMakeLists.txt
-
4resources/3rdparty/include_cpptemplate.cmake
-
14resources/3rdparty/include_cudd.cmake
-
9resources/3rdparty/include_glpk.cmake
-
0resources/3rdparty/include_xerces.cmake.save
-
49resources/3rdparty/include_xerces.cmake.save.1
-
43resources/cmake/find_modules/FindCLN.cmake
-
54resources/cmake/find_modules/FindGiNaC.cmake
-
28resources/cmake/macros/export.cmake
-
59resources/cmake/macros/imported.cmake
-
21resources/cmake/stormConfig.cmake.in
-
2src/CMakeLists.txt
-
92src/storm-dft-cli/storm-dyftee.cpp
-
2src/storm-dft/CMakeLists.txt
-
1src/storm-dft/modelchecker/dft/DFTModelChecker.cpp
-
18src/storm-dft/parser/DFTGalileoParser.cpp
-
5src/storm-dft/parser/DFTGalileoParser.h
-
167src/storm-dft/parser/DFTJsonParser.cpp
-
48src/storm-dft/parser/DFTJsonParser.h
-
14src/storm-dft/settings/modules/DFTSettings.cpp
-
16src/storm-dft/settings/modules/DFTSettings.h
-
11src/storm-dft/storage/dft/DFT.cpp
-
19src/storm-dft/storage/dft/DFT.h
-
78src/storm-dft/storage/dft/DFTBuilder.cpp
-
63src/storm-dft/storage/dft/DFTBuilder.h
-
23src/storm-dft/storage/dft/DFTIsomorphism.h
-
15src/storm-dft/storage/dft/DFTLayoutInfo.h
-
14src/storm-dft/storage/dft/DFTState.cpp
-
3src/storm-dft/storage/dft/elements/DFTBE.h
-
46src/storm-dft/storage/dft/elements/DFTDependency.h
-
4src/storm-dft/storage/dft/elements/DFTElement.cpp
-
15src/storm-dft/storage/dft/elements/DFTPand.h
-
17src/storm-dft/storage/dft/elements/DFTPor.h
-
9src/storm-dft/storage/dft/elements/DFTRestriction.h
-
629src/storm-dft/transformations/DftToGspnTransformator.cpp
-
163src/storm-dft/transformations/DftToGspnTransformator.h
-
12src/storm-gspn-cli/storm-gspn.cpp
-
102src/storm-gspn/builder/JaniGSPNBuilder.h
-
66src/storm-gspn/storage/gspn/GSPN.cpp
-
37src/storm-gspn/storage/gspn/GSPN.h
-
49src/storm-gspn/storage/gspn/GspnBuilder.cpp
-
16src/storm-gspn/storage/gspn/GspnBuilder.h
-
17src/storm-gspn/storage/gspn/PlacementInfo.h
-
3src/storm-gspn/storage/gspn/Transition.h
-
18src/storm-gspn/storage/gspn/TransitionPartition.h
-
61src/storm-gspn/storm-gspn.h
-
5src/storm-pgcl-cli/storm-pgcl.cpp
-
18src/storm-pgcl/builder/JaniProgramGraphBuilder.h
-
7src/storm/CMakeLists.txt
-
11src/storm/cli/cli.cpp
-
2src/storm/generator/NextStateGenerator.cpp
-
2src/storm/logic/Formula.h
-
16src/storm/settings/modules/GSPNExportSettings.cpp
-
11src/storm/settings/modules/GSPNExportSettings.h
-
2src/storm/storage/BitVector.cpp
-
6src/storm/storage/expressions/Expression.cpp
-
15src/storm/storage/jani/JSONExporter.cpp
-
8src/storm/storage/jani/JSONExporter.h
-
9src/storm/storage/jani/OrderedAssignments.cpp
-
2src/storm/storage/jani/OrderedAssignments.h
-
11src/storm/utility/storm.cpp
-
10src/storm/utility/storm.h
-
2src/test/CMakeLists.txt
-
39stormpy/CMakeLists.txt
-
1stormpy/MANIFEST.in
-
3stormpy/lib/.gitignore
-
70stormpy/lib/stormpy/__init__.py
-
2stormpy/lib/stormpy/expressions/__init__.py
-
2stormpy/lib/stormpy/info/__init__.py
-
2stormpy/lib/stormpy/logic/__init__.py
-
3stormpy/lib/stormpy/storage/__init__.py
-
36stormpy/lib/stormpy/storage/action.py
-
34stormpy/lib/stormpy/storage/state.py
-
24stormpy/resources/pybind11/.appveyor.yml
-
33stormpy/resources/pybind11/.gitignore
-
3stormpy/resources/pybind11/.gitmodules
-
44stormpy/resources/pybind11/.travis.yml
-
170stormpy/resources/pybind11/CMakeLists.txt
-
37stormpy/resources/pybind11/CONTRIBUTING.md
-
36stormpy/resources/pybind11/LICENSE
-
1stormpy/resources/pybind11/MANIFEST.in
-
114stormpy/resources/pybind11/README.md
-
11stormpy/resources/pybind11/docs/_static/theme_overrides.css
-
1613stormpy/resources/pybind11/docs/advanced.rst
-
309stormpy/resources/pybind11/docs/basics.rst
-
90stormpy/resources/pybind11/docs/benchmark.py
-
96stormpy/resources/pybind11/docs/benchmark.rst
-
172stormpy/resources/pybind11/docs/changelog.rst
-
334stormpy/resources/pybind11/docs/classes.rst
-
53stormpy/resources/pybind11/docs/compiling.rst
-
308stormpy/resources/pybind11/docs/conf.py
-
208stormpy/resources/pybind11/docs/faq.rst
-
24stormpy/resources/pybind11/docs/index.rst
-
87stormpy/resources/pybind11/docs/intro.rst
-
23stormpy/resources/pybind11/docs/limitations.rst
-
BINstormpy/resources/pybind11/docs/pybind11-logo.png
@ -0,0 +1,25 @@ |
|||
project(carlext) |
|||
cmake_minimum_required(VERSION 3.3) |
|||
include(ExternalProject) |
|||
|
|||
option(STORM_3RDPARTY_BINARY_DIR "3rd party bin dir") |
|||
|
|||
message(STORM_3RDPARTY_BINARY_DIR: ${STORM_3RDPARTY_BINARY_DIR}) |
|||
|
|||
ExternalProject_Add(carl-config |
|||
GIT_REPOSITORY https://github.com/smtrat/carl |
|||
GIT_TAG master |
|||
PREFIX here |
|||
SOURCE_DIR source_dir |
|||
BINARY_DIR ${STORM_3RDPARTY_BINARY_DIR}/carl |
|||
CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DBOOST_INCLUDEDIR=${Boost_INCLUDE_DIRS} -DBOOST_LIBRARYDIR=${Boost_LIBRARY_DIRS} -DBoost_NO_SYSTEM_PATHS=1 -DEXPORT_TO_CMAKE=0 -DUSE_CLN_NUMBERS=1 -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=1 -DCMAKE_INSTALL_PREFIX:PATH=${STORM_3RDPARTY_BINARY_DIR}/carl |
|||
BUILD_IN_SOURCE 0 |
|||
LOG_UPDATE OFF |
|||
LOG_CONFIGURE OFF |
|||
BUILD_COMMAND "" # Disable build step. |
|||
INSTALL_COMMAND "" # Disable install step too. |
|||
) |
|||
add_custom_target(build-carl) |
|||
add_dependencies(build-carl carl-config) |
|||
|
|||
message("done") |
|||
@ -0,0 +1,49 @@ |
|||
if(USE_XERCESC) |
|||
set(XERCESC_FIND_QUIETLY ON) |
|||
set(XERCESC_STATIC OFF) |
|||
find_package(XercesC QUIET REQUIRED) |
|||
if(XERCESC_FOUND) |
|||
message(STATUS "Storm - Use system version of xerces.") |
|||
else() |
|||
message(STATUS "Storm - Use shipped version of xerces.") |
|||
set(XERCESC_LIB_DIR ${STORM_3RDPARTY_BINARY_DIR}/xercesc-3.1.2/lib) |
|||
ExternalProject_Add( |
|||
xercesc |
|||
SOURCE_DIR ${STORM_3RDPARTY_SOURCE_DIR}/xercesc-3.1.2 |
|||
CONFIGURE_COMMAND ${STORM_3RDPARTY_SOURCE_DIR}/xercesc-3.1.2/configure --prefix=${STORM_3RDPARTY_BINARY_DIR}/xercesc-3.1.2 --libdir=${XERCESC_LIB_DIR} CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} CFLAGS=-O3 CXXFLAGS=-O3 |
|||
PREFIX ${STORM_3RDPARTY_BINARY_DIR}/xercesc-3.1.2 |
|||
BUILD_COMMAND make |
|||
BUILD_IN_SOURCE 0 |
|||
LOG_CONFIGURE ON |
|||
LOG_BUILD ON |
|||
LOG_INSTALL ON |
|||
BUILD_BYPRODUCTS ${XERCESC_LIB_DIR}/libxerces-c${DYNAMIC_EXT} ${XERCESC_LIB_DIR}/libxerces-c${STATIC_EXT} |
|||
) |
|||
|
|||
set(XERCESC_ROOT ${STORM_3RDPARTY_BINARY_DIR}/xercesc-3.1.2) |
|||
set(XERCESC_INCLUDE ${XERCESC_ROOT}/include) |
|||
set(XERCESC_LIBRARY_PATH ${XERCESC_LIB_DIR}) |
|||
|
|||
if(BUILD_STATIC) |
|||
set(XERCESC_LIBRARIES ${XERCESC_LIBRARY_PATH}/libxerces-c${STATIC_EXT}) |
|||
else() |
|||
set(XERCESC_LIBRARIES ${XERCESC_LIBRARY_PATH}/libxerces-c${DYNAMIC_EXT}) |
|||
endif() |
|||
|
|||
add_dependencies(resources xercesc) |
|||
endif() |
|||
|
|||
message (STATUS "Storm - Linking with xercesc.") |
|||
set(STORM_HAVE_XERCES ON) |
|||
include_directories(${XERCESC_INCLUDE}) |
|||
if(APPLE) |
|||
FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation ) |
|||
FIND_LIBRARY(CORESERVICES_LIBRARY CoreServices ) |
|||
mark_as_advanced(COREFOUNDATION_LIBRARY) |
|||
mark_as_advanced(CORESERVICES_LIBRARY) |
|||
endif() |
|||
find_package(CURL) |
|||
list(APPEND STORM_GSPN_LINK_LIBRARIES ${XERCESC_LIBRARIES} ${COREFOUNDATION_LIBRARY} ${CORESERVICES_LIBRARY} ${CURL_LIBRARIES}) |
|||
else() |
|||
message (WARNING "Storm - Building without Xerces disables parsing XML formats (for GSPNs)") |
|||
endif(USE_XERCESC) |
|||
@ -1,43 +0,0 @@ |
|||
# - Try to find libcln |
|||
# Once done this will define |
|||
# CLN_FOUND - System has cln |
|||
# CLN_INCLUDE_DIR - The cln include directory |
|||
# CLN_LIBRARIES - The libraries needed to use cln |
|||
# CLN_VERSION_STRING - The version of cln ("major.minor.patch") |
|||
|
|||
# use pkg-config to get the directories and then use these values |
|||
# in the find_path() and find_library() calls |
|||
find_package(PkgConfig QUIET) |
|||
PKG_CHECK_MODULES(PC_CLN QUIET cln) |
|||
|
|||
find_path(CLN_INCLUDE_DIR NAMES cln/cln.h |
|||
HINTS |
|||
${PC_CLN_INCLUDEDIR} |
|||
${PC_CLN_INCLUDE_DIRS} |
|||
PATH_SUFFIXES cln |
|||
) |
|||
|
|||
find_library(CLN_LIBRARIES NAMES cln |
|||
HINTS |
|||
${PC_CLN_LIBDIR} |
|||
${PC_CLN_LIBRARY_DIRS} |
|||
) |
|||
|
|||
if(PC_CLN_VERSION) |
|||
set(CLN_VERSION_STRING ${PC_CLN_VERSION}) |
|||
elseif(CLN_INCLUDE_DIR AND EXISTS "${CLN_INCLUDE_DIR}/version.h") |
|||
file(STRINGS "${CLN_INCLUDE_DIR}/version.h" cln_version |
|||
REGEX "^#define[\t ]+CL_VERSION[\t ]+.+") |
|||
string(REGEX REPLACE "^#define[\t ]+CL_VERSION[\t ]+(.+)" "\\1" |
|||
CLN_VERSION_STRING "${cln_version}") |
|||
unset(cln_version) |
|||
endif() |
|||
|
|||
# handle the QUIETLY and REQUIRED arguments and set LIBXML2_FOUND to TRUE if |
|||
# all listed variables are TRUE |
|||
include(FindPackageHandleStandardArgs) |
|||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CLN |
|||
REQUIRED_VARS CLN_LIBRARIES CLN_INCLUDE_DIR |
|||
VERSION_VAR CLN_VERSION_STRING) |
|||
|
|||
mark_as_advanced(CLN_INCLUDE_DIR CLN_LIBRARIES) |
|||
@ -1,54 +0,0 @@ |
|||
# - Try to find libginac |
|||
# Once done this will define |
|||
# GINAC_FOUND - System has ginac |
|||
# GINAC_INCLUDE_DIR - The ginac include directory |
|||
# GINAC_LIBRARIES - The libraries needed to use ginac |
|||
# GINAC_VERSION_STRING - The version of ginac ("major.minor.micro") |
|||
|
|||
# use pkg-config to get the directories and then use these values |
|||
# in the find_path() and find_library() calls |
|||
find_package(PkgConfig QUIET) |
|||
PKG_CHECK_MODULES(PC_GINAC QUIET ginac) |
|||
|
|||
find_path(GINAC_INCLUDE_DIR NAMES ginac.h |
|||
HINTS |
|||
${PC_GINAC_INCLUDEDIR} |
|||
${PC_GINAC_INCLUDE_DIRS} |
|||
PATH_SUFFIXES ginac |
|||
) |
|||
|
|||
find_library(GINAC_LIBRARIES NAMES ginac |
|||
HINTS |
|||
${PC_GINAC_LIBDIR} |
|||
${PC_GINAC_LIBRARY_DIRS} |
|||
) |
|||
|
|||
if(PC_GINAC_VERSION) |
|||
set(GINAC_VERSION_STRING ${PC_GINAC_VERSION}) |
|||
elseif(GINAC_INCLUDE_DIR AND EXISTS "${GINAC_INCLUDE_DIR}/ginac.h") |
|||
file(STRINGS "${GINAC_INCLUDE_DIR}/ginac.h" ginac_major_version |
|||
REGEX "^#define[\t ]+GINACLIB_MAJOR_VERSION[\t ]+.+") |
|||
file(STRINGS "${GINAC_INCLUDE_DIR}/ginac.h" ginac_minor_version |
|||
REGEX "^#define[\t ]+GINACLIB_MINOR_VERSION[\t ]+.+") |
|||
file(STRINGS "${GINAC_INCLUDE_DIR}/ginac.h" ginac_micro_version |
|||
REGEX "^#define[\t ]+GINACLIB_MICRO_VERSION[\t ]+.+") |
|||
string(REGEX REPLACE "^#define[\t ]+GINACLIB_MAJOR_VERSION[\t ]+(.+)" "\\1" |
|||
ginac_major_version "${ginac_major_version}") |
|||
string(REGEX REPLACE "^#define[\t ]+GINACLIB_MINOR_VERSION[\t ]+(.+)" "\\1" |
|||
ginac_minor_version "${ginac_minor_version}") |
|||
string(REGEX REPLACE "^#define[\t ]+GINACLIB_MICRO_VERSION[\t ]+(.+)" "\\1" |
|||
ginac_micro_version "${ginac_micro_version}") |
|||
set(GINAC_VERSION_STRING "${ginac_major_version}.${ginac_minor_version}.${ginac_micro_version}") |
|||
unset(ginac_major_version) |
|||
unset(ginac_minor_version) |
|||
unset(ginac_micro_version) |
|||
endif() |
|||
|
|||
# handle the QUIETLY and REQUIRED arguments and set GINAC_FOUND to TRUE if |
|||
# all listed variables are TRUE |
|||
include(FindPackageHandleStandardArgs) |
|||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GINAC |
|||
REQUIRED_VARS GINAC_LIBRARIES GINAC_INCLUDE_DIR |
|||
VERSION_VAR GINAC_VERSION_STRING) |
|||
|
|||
mark_as_advanced(GINAC_INCLUDE_DIR GINAC_LIBRARIES) |
|||
@ -0,0 +1,28 @@ |
|||
|
|||
# Add all targets to the build-tree export set |
|||
export(TARGETS ${STORM_TARGETS} FILE "${PROJECT_BINARY_DIR}/stormTargets.cmake") |
|||
|
|||
message(STATUS "Registered with cmake") |
|||
# Export the package for use from the build-tree |
|||
# (this registers the build-tree with a global CMake-registry) |
|||
export(PACKAGE storm) |
|||
|
|||
set(DEP_TARGETS "") |
|||
foreach(dt ${STORM_DEP_TARGETS}) |
|||
export_target(DEP_TARGETS ${dt}) |
|||
endforeach() |
|||
|
|||
set(EXP_OPTIONS "") |
|||
foreach(option ${EXPORTED_OPTIONS}) |
|||
set(EXP_OPTIONS "${EXP_OPTIONS}\nset(${option} \"${${option}}\")") |
|||
endforeach() |
|||
|
|||
include(CMakePackageConfigHelpers) |
|||
|
|||
set(CONF_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/include/") |
|||
configure_package_config_file( |
|||
resources/cmake/stormConfig.cmake.in |
|||
${PROJECT_BINARY_DIR}/stormConfig.cmake |
|||
INSTALL_DESTINATION ${CMAKE_INSTALL_DIR} |
|||
PATH_VARS INCLUDE_INSTALL_DIR #SYSCONFIG_INSTALL_DIR |
|||
) |
|||
@ -0,0 +1,59 @@ |
|||
# copied from CARL |
|||
|
|||
|
|||
macro(add_imported_library_interface name include) |
|||
add_library(${name} INTERFACE IMPORTED) |
|||
set_target_properties(${name} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include}") |
|||
endmacro(add_imported_library_interface) |
|||
|
|||
macro(add_imported_library name type lib include) |
|||
# Workaround from https://cmake.org/Bug/view.php?id=15052 |
|||
file(MAKE_DIRECTORY "${include}") |
|||
if("${lib}" STREQUAL "") |
|||
if("${type}" STREQUAL "SHARED") |
|||
add_library(${name} INTERFACE IMPORTED) |
|||
set_target_properties(${name} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include}") |
|||
endif() |
|||
else() |
|||
add_library(${name}_${type} ${type} IMPORTED) |
|||
set_target_properties(${name}_${type} PROPERTIES IMPORTED_LOCATION "${lib}") |
|||
set_target_properties(${name}_${type} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include}") |
|||
endif() |
|||
endmacro(add_imported_library) |
|||
|
|||
macro(export_option name) |
|||
list(APPEND EXPORTED_OPTIONS "${name}") |
|||
endmacro(export_option) |
|||
|
|||
macro(export_target output TARGET) |
|||
get_target_property(TYPE ${TARGET} TYPE) |
|||
if(TYPE STREQUAL "SHARED_LIBRARY") |
|||
get_target_property(LOCATION ${TARGET} IMPORTED_LOCATION) |
|||
get_target_property(INCLUDE ${TARGET} INTERFACE_INCLUDE_DIRECTORIES) |
|||
set(${output} "${${output}} |
|||
add_library(${TARGET} SHARED IMPORTED) |
|||
set_target_properties(${TARGET} PROPERTIES IMPORTED_LOCATION \"${LOCATION}\") |
|||
set_target_properties(${TARGET} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${INCLUDE}\") |
|||
") |
|||
elseif(TYPE STREQUAL "STATIC_LIBRARY") |
|||
get_target_property(LOCATION ${TARGET} IMPORTED_LOCATION) |
|||
get_target_property(INCLUDE ${TARGET} INTERFACE_INCLUDE_DIRECTORIES) |
|||
set(${output} "${${output}} |
|||
add_library(${TARGET} STATIC IMPORTED) |
|||
set_target_properties(${TARGET} PROPERTIES IMPORTED_LOCATION \"${LOCATION}\") |
|||
set_target_properties(${TARGET} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${INCLUDE}\") |
|||
") |
|||
if(NOT "${ARGN}" STREQUAL "") |
|||
set(${output} "${${output}}set_target_properties(${TARGET} PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES \"${ARGN}\") |
|||
") |
|||
endif() |
|||
elseif(TYPE STREQUAL "INTERFACE_LIBRARY") |
|||
get_target_property(INCLUDE ${TARGET} INTERFACE_INCLUDE_DIRECTORIES) |
|||
set(${output} "${${output}} |
|||
add_library(${TARGET} INTERFACE IMPORTED) |
|||
set_target_properties(${TARGET} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${INCLUDE}\") |
|||
") |
|||
else() |
|||
message(STATUS "Unknown type ${TYPE}") |
|||
endif() |
|||
endmacro(export_target) |
|||
@ -0,0 +1,21 @@ |
|||
set(storm_VERSION @storm_VERSION@) |
|||
|
|||
get_filename_component(storm_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) |
|||
|
|||
include("@carl_CMAKE_DIR@/carlConfig.cmake") |
|||
|
|||
@DEP_TARGETS@ |
|||
|
|||
@EXP_OPTIONS@ |
|||
|
|||
# Our library dependencies (contains definitions for IMPORTED targets) |
|||
if(NOT TARGET storm) |
|||
include("${storm_CMAKE_DIR}/stormTargets.cmake") |
|||
endif() |
|||
|
|||
@PACKAGE_INIT@ |
|||
|
|||
set(storm_INCLUDE_DIR "@CONF_INCLUDE_DIRS@") |
|||
|
|||
set(storm_LIBRARIES storm) |
|||
check_required_components(storm) |
|||
@ -0,0 +1,167 @@ |
|||
#include "DFTJsonParser.h"
|
|||
|
|||
#include <iostream>
|
|||
#include <fstream>
|
|||
#include <boost/algorithm/string.hpp>
|
|||
#include <boost/lexical_cast.hpp>
|
|||
#include <boost/algorithm/string/replace.hpp>
|
|||
#include "storm/storage/expressions/ExpressionManager.h"
|
|||
#include "storm/exceptions/NotImplementedException.h"
|
|||
#include "storm/exceptions/FileIoException.h"
|
|||
#include "storm/exceptions/NotSupportedException.h"
|
|||
#include "storm/utility/macros.h"
|
|||
|
|||
namespace storm { |
|||
namespace parser { |
|||
|
|||
template<typename ValueType> |
|||
storm::storage::DFT<ValueType> DFTJsonParser<ValueType>::parseJson(const std::string& filename) { |
|||
readFile(filename); |
|||
storm::storage::DFT<ValueType> dft = builder.build(); |
|||
STORM_LOG_DEBUG("Elements:" << std::endl << dft.getElementsString()); |
|||
STORM_LOG_DEBUG("Spare Modules:" << std::endl << dft.getSpareModulesString()); |
|||
return dft; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::string DFTJsonParser<ValueType>::stripQuotsFromName(std::string const& name) { |
|||
size_t firstQuots = name.find("\""); |
|||
size_t secondQuots = name.find("\"", firstQuots+1); |
|||
|
|||
if(firstQuots == std::string::npos) { |
|||
return name; |
|||
} else { |
|||
STORM_LOG_THROW(secondQuots != std::string::npos, storm::exceptions::FileIoException, "No ending quotation mark found in " << name); |
|||
return name.substr(firstQuots+1,secondQuots-1); |
|||
} |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::string DFTJsonParser<ValueType>::getString(json const& structure, std::string const& errorInfo) { |
|||
STORM_LOG_THROW(structure.is_string(), storm::exceptions::FileIoException, "Expected a string in " << errorInfo << ", got '" << structure.dump() << "'"); |
|||
return structure.front(); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
std::string DFTJsonParser<ValueType>::parseNodeIdentifier(std::string const& name) { |
|||
return boost::replace_all_copy(name, "'", "__prime__"); |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
void DFTJsonParser<ValueType>::readFile(const std::string& filename) { |
|||
STORM_LOG_DEBUG("Parsing from JSON"); |
|||
|
|||
std::ifstream file; |
|||
file.exceptions ( std::ifstream::failbit ); |
|||
try { |
|||
file.open(filename); |
|||
} |
|||
catch (std::ifstream::failure e) { |
|||
STORM_LOG_THROW(false, storm::exceptions::FileIoException, "Exception during file opening on " << filename << "."); |
|||
return; |
|||
} |
|||
file.exceptions( std::ifstream::goodbit ); |
|||
|
|||
json parsedJson; |
|||
parsedJson << file; |
|||
file.close(); |
|||
|
|||
// Start by building mapping from ids to names
|
|||
std::map<std::string, std::string> nameMapping; |
|||
for (auto& element: parsedJson) { |
|||
if (element.at("classes") != "") { |
|||
json data = element.at("data"); |
|||
std::string id = data.at("id"); |
|||
std::string name = data.at("name"); |
|||
nameMapping[id] = name; |
|||
} |
|||
} |
|||
|
|||
// TODO: avoid hack
|
|||
std::string toplevelId = nameMapping["1"]; |
|||
|
|||
for (auto& element : parsedJson) { |
|||
bool success = true; |
|||
if (element.at("classes") == "") { |
|||
continue; |
|||
} |
|||
json data = element.at("data"); |
|||
std::string name = data.at("name"); |
|||
std::vector<std::string> childNames; |
|||
if (data.count("children") > 0) { |
|||
for (auto& child : data.at("children")) { |
|||
childNames.push_back(nameMapping[child]); |
|||
} |
|||
} |
|||
|
|||
std::string type = getString(element.at("classes"), "classes"); |
|||
|
|||
if(type == "and") { |
|||
success = builder.addAndElement(name, childNames); |
|||
} else if (type == "or") { |
|||
success = builder.addOrElement(name, childNames); |
|||
} else if (type == "pand") { |
|||
success = builder.addPandElement(name, childNames); |
|||
} else if (type == "por") { |
|||
success = builder.addPorElement(name, childNames); |
|||
} else if (type == "spare") { |
|||
success = builder.addSpareElement(name, childNames); |
|||
} else if (type == "seq") { |
|||
success = builder.addSequenceEnforcer(name, childNames); |
|||
} else if (type== "fdep") { |
|||
success = builder.addDepElement(name, childNames, storm::utility::one<ValueType>()); |
|||
} else if (type== "pdep") { |
|||
ValueType probability = parseRationalExpression(data.at("prob")); |
|||
success = builder.addDepElement(name, childNames, probability); |
|||
} else if (type == "be") { |
|||
ValueType failureRate = parseRationalExpression(data.at("rate")); |
|||
ValueType dormancyFactor = parseRationalExpression(data.at("dorm")); |
|||
success = builder.addBasicElement(name, failureRate, dormancyFactor); |
|||
} else { |
|||
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Type name: " << type << " not recognized."); |
|||
success = false; |
|||
} |
|||
|
|||
// Set layout positions
|
|||
json position = element.at("position"); |
|||
double x = position.at("x"); |
|||
double y = position.at("y"); |
|||
builder.addLayoutInfo(name, x / 7, y / 7); |
|||
STORM_LOG_THROW(success, storm::exceptions::FileIoException, "Error while adding element '" << element << "'."); |
|||
} |
|||
|
|||
if(!builder.setTopLevel(toplevelId)) { |
|||
STORM_LOG_THROW(false, storm::exceptions::FileIoException, "Top level id unknown."); |
|||
} |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
ValueType DFTJsonParser<ValueType>::parseRationalExpression(std::string const& expr) { |
|||
STORM_LOG_ASSERT(false, "Specialized method should be called."); |
|||
return 0; |
|||
} |
|||
|
|||
template<> |
|||
double DFTJsonParser<double>::parseRationalExpression(std::string const& expr) { |
|||
return boost::lexical_cast<double>(expr); |
|||
} |
|||
|
|||
// Explicitly instantiate the class.
|
|||
template class DFTJsonParser<double>; |
|||
|
|||
#ifdef STORM_HAVE_CARL
|
|||
template<> |
|||
storm::RationalFunction DFTJsonParser<storm::RationalFunction>::parseRationalExpression(std::string const& expr) { |
|||
STORM_LOG_TRACE("Translating expression: " << expr); |
|||
storm::expressions::Expression expression = parser.parseFromString(expr); |
|||
STORM_LOG_TRACE("Expression: " << expression); |
|||
storm::RationalFunction rationalFunction = evaluator.asRational(expression); |
|||
STORM_LOG_TRACE("Parsed expression: " << rationalFunction); |
|||
return rationalFunction; |
|||
} |
|||
|
|||
template class DFTJsonParser<RationalFunction>; |
|||
#endif
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
#pragma once |
|||
|
|||
#include <map> |
|||
|
|||
#include "storm/storage/expressions/ExpressionManager.h" |
|||
#include "storm/parser/ExpressionParser.h" |
|||
#include "storm/storage/expressions/ExpressionEvaluator.h" |
|||
|
|||
#include "storm-dft/storage/dft/DFT.h" |
|||
#include "storm-dft/storage/dft/DFTBuilder.h" |
|||
|
|||
// JSON parser |
|||
#include "json.hpp" |
|||
|
|||
using json = nlohmann::json; |
|||
|
|||
namespace storm { |
|||
namespace parser { |
|||
|
|||
template<typename ValueType> |
|||
class DFTJsonParser { |
|||
storm::storage::DFTBuilder<ValueType> builder; |
|||
|
|||
std::shared_ptr<storm::expressions::ExpressionManager> manager; |
|||
|
|||
storm::parser::ExpressionParser parser; |
|||
|
|||
storm::expressions::ExpressionEvaluator<ValueType> evaluator; |
|||
|
|||
std::unordered_map<std::string, storm::expressions::Expression> identifierMapping; |
|||
|
|||
public: |
|||
DFTJsonParser() : manager(new storm::expressions::ExpressionManager()), parser(*manager), evaluator(*manager) { |
|||
} |
|||
|
|||
storm::storage::DFT<ValueType> parseJson(std::string const& filename); |
|||
|
|||
private: |
|||
void readFile(std::string const& filename); |
|||
|
|||
std::string stripQuotsFromName(std::string const& name); |
|||
std::string parseNodeIdentifier(std::string const& name); |
|||
std::string getString(json const& structure, std::string const& errorInfo); |
|||
|
|||
ValueType parseRationalExpression(std::string const& expr); |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
#pragma once |
|||
|
|||
namespace storm { |
|||
namespace storage { |
|||
struct DFTLayoutInfo { |
|||
DFTLayoutInfo() {}; |
|||
DFTLayoutInfo(double x, double y) : x(x), y(y) {}; |
|||
|
|||
// x location |
|||
double x = 0.0; |
|||
// y location |
|||
double y = 0.0; |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,629 @@ |
|||
#include "DftToGspnTransformator.h"
|
|||
#include "storm/exceptions/NotImplementedException.h"
|
|||
#include <memory>
|
|||
|
|||
namespace storm { |
|||
namespace transformations { |
|||
namespace dft { |
|||
|
|||
// Prevent some magic constants
|
|||
static constexpr const uint64_t defaultPriority = 1; |
|||
static constexpr const uint64_t defaultCapacity = 1; |
|||
|
|||
template <typename ValueType> |
|||
DftToGspnTransformator<ValueType>::DftToGspnTransformator(storm::storage::DFT<ValueType> const& dft) : mDft(dft) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::transform() { |
|||
|
|||
builder.setGspnName("DftToGspnTransformation"); |
|||
|
|||
// Loop through every DFT element and draw them as a GSPN.
|
|||
drawGSPNElements(); |
|||
|
|||
// Draw restrictions into the GSPN (i.e. SEQ or MUTEX).
|
|||
//drawGSPNRestrictions();
|
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t DftToGspnTransformator<ValueType>::toplevelFailedPlaceId() { |
|||
assert(failedNodes.size() > mDft.getTopLevelIndex()); |
|||
return failedNodes[mDft.getTopLevelIndex()]; |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawGSPNElements() { |
|||
|
|||
|
|||
// Loop through every DFT element and draw them as a GSPN.
|
|||
for (std::size_t i = 0; i < mDft.nrElements(); i++) { |
|||
auto dftElement = mDft.getElement(i); |
|||
bool isRepresentative = mDft.isRepresentative(i); |
|||
|
|||
// Check which type the element is and call the corresponding drawing-function.
|
|||
switch (dftElement->type()) { |
|||
case storm::storage::DFTElementType::AND: |
|||
drawAND(std::static_pointer_cast<storm::storage::DFTAnd<ValueType> const>(dftElement), isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::OR: |
|||
drawOR(std::static_pointer_cast<storm::storage::DFTOr<ValueType> const>(dftElement), isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::VOT: |
|||
drawVOT(std::static_pointer_cast<storm::storage::DFTVot<ValueType> const>(dftElement), isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::PAND: |
|||
drawPAND(std::static_pointer_cast<storm::storage::DFTPand<ValueType> const>(dftElement), isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::SPARE: |
|||
drawSPARE(std::static_pointer_cast<storm::storage::DFTSpare<ValueType> const>(dftElement), isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::POR: |
|||
drawPOR(std::static_pointer_cast<storm::storage::DFTPor<ValueType> const>(dftElement), isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::SEQ: |
|||
drawSeq(std::static_pointer_cast<storm::storage::DFTSeq<ValueType> const>(dftElement)); |
|||
break; |
|||
case storm::storage::DFTElementType::MUTEX: |
|||
// No method call needed here. MUTEX only consists of restrictions, which are handled later.
|
|||
break; |
|||
case storm::storage::DFTElementType::BE: |
|||
drawBE(std::static_pointer_cast<storm::storage::DFTBE<ValueType> const>(dftElement), isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::CONSTF: |
|||
drawCONSTF(dftElement, isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::CONSTS: |
|||
drawCONSTS(dftElement, isRepresentative); |
|||
break; |
|||
case storm::storage::DFTElementType::PDEP: |
|||
drawPDEP(std::static_pointer_cast<storm::storage::DFTDependency<ValueType> const>(dftElement)); |
|||
break; |
|||
default: |
|||
STORM_LOG_ASSERT(false, "DFT type unknown."); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawBE(std::shared_ptr<storm::storage::DFTBE<ValueType> const> dftBE, bool isRepresentative) { |
|||
uint64_t beActive = builder.addPlace(defaultCapacity, isBEActive(dftBE) ? 1 : 0, dftBE->name() + STR_ACTIVATED); |
|||
activeNodes.emplace(dftBE->id(), beActive); |
|||
uint64_t beFailed = builder.addPlace(defaultCapacity, 0, dftBE->name() + STR_FAILED); |
|||
|
|||
double xcenter = mDft.getElementLayoutInfo(dftBE->id()).x; |
|||
double ycenter = mDft.getElementLayoutInfo(dftBE->id()).y; |
|||
builder.setPlaceLayoutInfo(beActive, storm::gspn::LayoutInfo(xcenter - 3.0, ycenter)); |
|||
builder.setPlaceLayoutInfo(beFailed, storm::gspn::LayoutInfo(xcenter + 3.0, ycenter)); |
|||
|
|||
|
|||
uint64_t disabledNode = 0; |
|||
if (!smart || dftBE->nrRestrictions() > 0) { |
|||
disabledNode = addDisabledPlace(dftBE); |
|||
} |
|||
|
|||
uint64_t unavailableNode = 0; |
|||
if (!smart || isRepresentative) { |
|||
unavailableNode = addUnavailableNode(dftBE, storm::gspn::LayoutInfo(xcenter+9.0, ycenter)); |
|||
} |
|||
|
|||
assert(failedNodes.size() == dftBE->id()); |
|||
failedNodes.push_back(beFailed); |
|||
uint64_t tActive = builder.addTimedTransition(defaultPriority, dftBE->activeFailureRate(), dftBE->name() + "_activeFailing"); |
|||
builder.setTransitionLayoutInfo(tActive, storm::gspn::LayoutInfo(xcenter, ycenter + 3.0)); |
|||
builder.addInputArc(beActive, tActive); |
|||
builder.addInhibitionArc(beFailed, tActive); |
|||
builder.addOutputArc(tActive, beActive); |
|||
builder.addOutputArc(tActive, beFailed); |
|||
uint64_t tPassive = builder.addTimedTransition(defaultPriority, dftBE->passiveFailureRate(), dftBE->name() + "_passiveFailing"); |
|||
builder.setTransitionLayoutInfo(tPassive, storm::gspn::LayoutInfo(xcenter, ycenter - 3.0)); |
|||
builder.addInhibitionArc(beActive, tPassive); |
|||
builder.addInhibitionArc(beFailed, tPassive); |
|||
builder.addOutputArc(tPassive, beFailed); |
|||
|
|||
if (!smart || dftBE->nrRestrictions() > 0) { |
|||
builder.addInhibitionArc(disabledNode, tActive); |
|||
builder.addInhibitionArc(disabledNode, tPassive); |
|||
} |
|||
|
|||
if (!smart || isRepresentative) { |
|||
builder.addOutputArc(tActive, unavailableNode); |
|||
builder.addOutputArc(tPassive, unavailableNode); |
|||
} |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawAND(std::shared_ptr<storm::storage::DFTAnd<ValueType> const> dftAnd, bool isRepresentative) { |
|||
uint64_t nodeFailed = builder.addPlace(defaultCapacity, 0, dftAnd->name() + STR_FAILED); |
|||
assert(failedNodes.size() == dftAnd->id()); |
|||
failedNodes.push_back(nodeFailed); |
|||
|
|||
double xcenter = mDft.getElementLayoutInfo(dftAnd->id()).x; |
|||
double ycenter = mDft.getElementLayoutInfo(dftAnd->id()).y; |
|||
builder.setPlaceLayoutInfo(nodeFailed, storm::gspn::LayoutInfo(xcenter, ycenter-3.0)); |
|||
|
|||
uint64_t unavailableNode = 0; |
|||
if (isRepresentative) { |
|||
unavailableNode = addUnavailableNode(dftAnd, storm::gspn::LayoutInfo(xcenter+6.0, ycenter-3.0)); |
|||
} |
|||
|
|||
|
|||
uint64_t tAndFailed = builder.addImmediateTransition( getFailPriority(dftAnd) , 0.0, dftAnd->name() + STR_FAILING ); |
|||
builder.setTransitionLayoutInfo(tAndFailed, storm::gspn::LayoutInfo(xcenter, ycenter+3.0)); |
|||
builder.addInhibitionArc(nodeFailed, tAndFailed); |
|||
builder.addOutputArc(tAndFailed, nodeFailed); |
|||
if (isRepresentative) { |
|||
builder.addOutputArc(tAndFailed, unavailableNode); |
|||
} |
|||
for(auto const& child : dftAnd->children()) { |
|||
assert(failedNodes.size() > child->id()); |
|||
builder.addInputArc(failedNodes[child->id()], tAndFailed); |
|||
builder.addOutputArc(tAndFailed, failedNodes[child->id()]); |
|||
} |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawOR(std::shared_ptr<storm::storage::DFTOr<ValueType> const> dftOr, bool isRepresentative) { |
|||
uint64_t nodeFailed = builder.addPlace(defaultCapacity, 0, dftOr->name() + STR_FAILED); |
|||
assert(failedNodes.size() == dftOr->id()); |
|||
failedNodes.push_back(nodeFailed); |
|||
|
|||
double xcenter = mDft.getElementLayoutInfo(dftOr->id()).x; |
|||
double ycenter = mDft.getElementLayoutInfo(dftOr->id()).y; |
|||
builder.setPlaceLayoutInfo(nodeFailed, storm::gspn::LayoutInfo(xcenter, ycenter-3.0)); |
|||
|
|||
uint64_t unavailableNode = 0; |
|||
if (isRepresentative) { |
|||
unavailableNode = addUnavailableNode(dftOr, storm::gspn::LayoutInfo(xcenter+6.0, ycenter-3.0)); |
|||
} |
|||
|
|||
uint64_t i = 0; |
|||
for (auto const& child : dftOr->children()) { |
|||
uint64_t tNodeFailed = builder.addImmediateTransition( getFailPriority(dftOr), 0.0, dftOr->name() + STR_FAILING + std::to_string(i) ); |
|||
builder.setTransitionLayoutInfo(tNodeFailed, storm::gspn::LayoutInfo(xcenter-5.0+i*3.0, ycenter+3.0)); |
|||
builder.addInhibitionArc(nodeFailed, tNodeFailed); |
|||
builder.addOutputArc(tNodeFailed, nodeFailed); |
|||
if (isRepresentative) { |
|||
builder.addOutputArc(tNodeFailed, unavailableNode); |
|||
} |
|||
assert(failedNodes.size() > child->id()); |
|||
builder.addInputArc(failedNodes[child->id()], tNodeFailed); |
|||
builder.addOutputArc(tNodeFailed, failedNodes[child->id()]); |
|||
++i; |
|||
} |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawVOT(std::shared_ptr<storm::storage::DFTVot<ValueType> const> dftVot, bool isRepresentative) { |
|||
// TODO: finish layouting
|
|||
uint64_t nodeFailed = builder.addPlace(defaultCapacity, 0, dftVot->name() + STR_FAILED); |
|||
assert(failedNodes.size() == dftVot->id()); |
|||
failedNodes.push_back(nodeFailed); |
|||
|
|||
double xcenter = mDft.getElementLayoutInfo(dftVot->id()).x; |
|||
double ycenter = mDft.getElementLayoutInfo(dftVot->id()).y; |
|||
builder.setPlaceLayoutInfo(nodeFailed, storm::gspn::LayoutInfo(xcenter, ycenter-3.0)); |
|||
|
|||
uint64_t unavailableNode = 0; |
|||
if (isRepresentative) { |
|||
unavailableNode = addUnavailableNode(dftVot, storm::gspn::LayoutInfo(xcenter+6.0, ycenter-3.0)); |
|||
} |
|||
|
|||
uint64_t nodeCollector = builder.addPlace(dftVot->nrChildren(), 0, dftVot->name() + "_collector"); |
|||
builder.setPlaceLayoutInfo(nodeCollector, storm::gspn::LayoutInfo(xcenter, ycenter)); |
|||
|
|||
uint64_t tNodeFailed = builder.addImmediateTransition(getFailPriority(dftVot), 0.0, dftVot->name() + STR_FAILING); |
|||
builder.addOutputArc(tNodeFailed, nodeFailed); |
|||
if (isRepresentative) { |
|||
builder.addOutputArc(tNodeFailed, unavailableNode); |
|||
} |
|||
builder.addInhibitionArc(nodeFailed, tNodeFailed); |
|||
builder.addInputArc(nodeCollector, tNodeFailed, dftVot->threshold()); |
|||
builder.addOutputArc(tNodeFailed, nodeCollector, dftVot->threshold()); |
|||
uint64_t i = 0; |
|||
for (auto const& child : dftVot->children()) { |
|||
uint64_t childInhibPlace = builder.addPlace(1, 0, dftVot->name() + "_child_fail_inhib" + std::to_string(i)); |
|||
uint64_t tCollect = builder.addImmediateTransition(getFailPriority(dftVot), 0.0, dftVot->name() + "_child_collect" + std::to_string(i)); |
|||
builder.addOutputArc(tCollect, nodeCollector); |
|||
builder.addOutputArc(tCollect, childInhibPlace); |
|||
builder.addInhibitionArc(childInhibPlace, tCollect); |
|||
builder.addInputArc(failedNodes[child->id()], tCollect); |
|||
builder.addOutputArc(tCollect, failedNodes[child->id()]); |
|||
++i; |
|||
} |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawPAND(std::shared_ptr<storm::storage::DFTPand<ValueType> const> dftPand, bool isRepresentative) { |
|||
uint64_t nodeFailed = builder.addPlace(defaultCapacity, 0, dftPand->name() + STR_FAILED); |
|||
assert(failedNodes.size() == dftPand->id()); |
|||
failedNodes.push_back(nodeFailed); |
|||
|
|||
double xcenter = mDft.getElementLayoutInfo(dftPand->id()).x; |
|||
double ycenter = mDft.getElementLayoutInfo(dftPand->id()).y; |
|||
builder.setPlaceLayoutInfo(nodeFailed, storm::gspn::LayoutInfo(xcenter+3.0, ycenter-3.0)); |
|||
|
|||
uint64_t unavailableNode = 0; |
|||
if (!smart || isRepresentative) { |
|||
unavailableNode = addUnavailableNode(dftPand, storm::gspn::LayoutInfo(xcenter+9.0, ycenter-3.0)); |
|||
} |
|||
|
|||
uint64_t tNodeFailed = builder.addImmediateTransition(getFailPriority(dftPand), 0.0, dftPand->name() + STR_FAILING); |
|||
builder.setTransitionLayoutInfo(tNodeFailed, storm::gspn::LayoutInfo(xcenter+3.0, ycenter+3.0)); |
|||
builder.addInhibitionArc(nodeFailed, tNodeFailed); |
|||
builder.addOutputArc(tNodeFailed, nodeFailed); |
|||
if (!smart || isRepresentative) { |
|||
builder.addOutputArc(tNodeFailed, nodeFailed); |
|||
} |
|||
|
|||
if(dftPand->isInclusive()) { |
|||
// Inclusive PAND
|
|||
uint64_t nodeFS = builder.addPlace(defaultCapacity, 0, dftPand->name() + STR_FAILSAVE); |
|||
builder.setPlaceLayoutInfo(nodeFS, storm::gspn::LayoutInfo(xcenter-3.0, ycenter-3.0)); |
|||
|
|||
builder.addInhibitionArc(nodeFS, tNodeFailed); |
|||
for(auto const& child : dftPand->children()) { |
|||
builder.addInputArc(failedNodes[child->id()], tNodeFailed); |
|||
builder.addOutputArc(tNodeFailed, failedNodes[child->id()]); |
|||
} |
|||
for (uint64_t j = 1; j < dftPand->nrChildren(); ++j) { |
|||
uint64_t tfs = builder.addImmediateTransition(getFailPriority(dftPand), 0.0, dftPand->name() + STR_FAILSAVING + std::to_string(j)); |
|||
builder.setTransitionLayoutInfo(tfs, storm::gspn::LayoutInfo(xcenter-6.0+j*3.0, ycenter+3.0)); |
|||
|
|||
builder.addInputArc(failedNodes[dftPand->children().at(j)->id()], tfs); |
|||
builder.addOutputArc(tfs, failedNodes[dftPand->children().at(j)->id()]); |
|||
builder.addInhibitionArc(failedNodes[dftPand->children().at(j-1)->id()], tfs); |
|||
builder.addOutputArc(tfs, nodeFS); |
|||
builder.addInhibitionArc(nodeFS, tfs); |
|||
|
|||
} |
|||
} else { |
|||
// Exclusive PAND
|
|||
uint64_t fi = 0; |
|||
uint64_t tn = 0; |
|||
for(uint64_t j = 0; j < dftPand->nrChildren(); ++j) { |
|||
auto const& child = dftPand->children()[j]; |
|||
if (j > 0) { |
|||
builder.addInhibitionArc(failedNodes.at(child->id()), tn); |
|||
} |
|||
if (j != dftPand->nrChildren() - 1) { |
|||
tn = builder.addImmediateTransition(getFailPriority(dftPand), 0.0, dftPand->name() + STR_FAILING + "_" +std::to_string(j)); |
|||
builder.setTransitionLayoutInfo(tn, storm::gspn::LayoutInfo(xcenter-3.0, ycenter+3.0)); |
|||
} else { |
|||
tn = tNodeFailed; |
|||
} |
|||
builder.addInputArc(failedNodes.at(child->id()), tn); |
|||
builder.addOutputArc(tn, failedNodes.at(child->id())); |
|||
if (j > 0) { |
|||
builder.addInputArc(fi, tn); |
|||
} |
|||
if (j != dftPand->nrChildren() - 1) { |
|||
fi = builder.addPlace(defaultCapacity, 0, dftPand->name() + "_F_" + std::to_string(j)); |
|||
builder.setPlaceLayoutInfo(fi, storm::gspn::LayoutInfo(xcenter-3.0+j*3.0, ycenter)); |
|||
builder.addOutputArc(tn, fi); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawPOR(std::shared_ptr<storm::storage::DFTPor<ValueType> const> dftPor, bool isRepresentative) { |
|||
uint64_t nodeFailed = builder.addPlace(defaultCapacity, 0, dftPor->name() + STR_FAILED); |
|||
failedNodes.push_back(nodeFailed); |
|||
|
|||
double xcenter = mDft.getElementLayoutInfo(dftPor->id()).x; |
|||
double ycenter = mDft.getElementLayoutInfo(dftPor->id()).y; |
|||
builder.setPlaceLayoutInfo(nodeFailed, storm::gspn::LayoutInfo(xcenter+3.0, ycenter-3.0)); |
|||
|
|||
uint64_t unavailableNode = 0; |
|||
if (!smart || isRepresentative) { |
|||
unavailableNode = addUnavailableNode(dftPor, storm::gspn::LayoutInfo(xcenter+9.0, ycenter-3.0)); |
|||
} |
|||
|
|||
uint64_t tfail = builder.addImmediateTransition(getFailPriority(dftPor), 0.0, dftPor->name() + STR_FAILING); |
|||
builder.setTransitionLayoutInfo(tfail, storm::gspn::LayoutInfo(xcenter+3.0, ycenter+3.0)); |
|||
builder.addOutputArc(tfail, nodeFailed); |
|||
builder.addInhibitionArc(nodeFailed, tfail); |
|||
|
|||
builder.addInputArc(failedNodes.at(dftPor->children().front()->id()), tfail); |
|||
builder.addOutputArc(tfail, failedNodes.at(dftPor->children().front()->id())); |
|||
|
|||
if(!smart || isRepresentative) { |
|||
builder.addOutputArc(tfail, unavailableNode); |
|||
} |
|||
|
|||
if(dftPor->isInclusive()) { |
|||
// Inclusive POR
|
|||
uint64_t nodeFS = builder.addPlace(defaultCapacity, 0, dftPor->name() + STR_FAILSAVE); |
|||
builder.setPlaceLayoutInfo(nodeFS, storm::gspn::LayoutInfo(xcenter-3.0, ycenter-3.0)); |
|||
|
|||
builder.addInhibitionArc(nodeFS, tfail); |
|||
uint64_t j = 0; |
|||
for (auto const& child : dftPor->children()) { |
|||
if(j > 0) { |
|||
uint64_t tfailsf = builder.addImmediateTransition(getFailPriority(dftPor), 0.0, dftPor->name() + STR_FAILSAVING + std::to_string(j)); |
|||
builder.setTransitionLayoutInfo(tfailsf, storm::gspn::LayoutInfo(xcenter-3.0+j*3.0, ycenter+3.0)); |
|||
builder.addInputArc(failedNodes.at(child->id()), tfailsf); |
|||
builder.addOutputArc(tfailsf, failedNodes.at(child->id())); |
|||
builder.addOutputArc(tfailsf, nodeFS); |
|||
builder.addInhibitionArc(nodeFS, tfailsf); |
|||
builder.addInhibitionArc(failedNodes.at(dftPor->children().front()->id()), tfailsf); |
|||
} |
|||
|
|||
++j; |
|||
} |
|||
} else { |
|||
// Exclusive POR
|
|||
uint64_t j = 0; |
|||
for (auto const& child : dftPor->children()) { |
|||
if(j > 0) { |
|||
builder.addInhibitionArc(failedNodes.at(child->id()), tfail); |
|||
} |
|||
++j; |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawSPARE(std::shared_ptr<storm::storage::DFTSpare<ValueType> const> dftSpare, bool isRepresentative) { |
|||
|
|||
uint64_t nodeFailed = builder.addPlace(defaultCapacity, 0, dftSpare->name() + STR_FAILED); |
|||
failedNodes.push_back(nodeFailed); |
|||
|
|||
double xcenter = mDft.getElementLayoutInfo(dftSpare->id()).x; |
|||
double ycenter = mDft.getElementLayoutInfo(dftSpare->id()).y; |
|||
builder.setPlaceLayoutInfo(nodeFailed, storm::gspn::LayoutInfo(xcenter+10.0, ycenter-8.0)); |
|||
|
|||
uint64_t unavailableNode = 0; |
|||
if (isRepresentative) { |
|||
unavailableNode = addUnavailableNode(dftSpare, storm::gspn::LayoutInfo(xcenter+16.0, ycenter-8.0)); |
|||
} |
|||
uint64_t spareActive = builder.addPlace(defaultCapacity, isBEActive(dftSpare) ? 1 : 0, dftSpare->name() + STR_ACTIVATED); |
|||
builder.setPlaceLayoutInfo(spareActive, storm::gspn::LayoutInfo(xcenter-20.0, ycenter-8.0)); |
|||
activeNodes.emplace(dftSpare->id(), spareActive); |
|||
|
|||
|
|||
std::vector<uint64_t> cucNodes; |
|||
std::vector<uint64_t> considerNodes; |
|||
std::vector<uint64_t> nextclTransitions; |
|||
std::vector<uint64_t> nextconsiderTransitions; |
|||
uint64_t j = 0; |
|||
for(auto const& child : dftSpare->children()) { |
|||
if (j > 0) { |
|||
size_t nodeConsider = builder.addPlace(defaultCapacity, 0, dftSpare->name()+ "_consider_" + child->name()); |
|||
considerNodes.push_back(nodeConsider); |
|||
builder.setPlaceLayoutInfo(nodeConsider, storm::gspn::LayoutInfo(xcenter-15.0+j*14.0, ycenter-8.0)); |
|||
|
|||
builder.addOutputArc(nextclTransitions.back(), considerNodes.back(), 1); |
|||
if (j > 1) { |
|||
builder.addOutputArc(nextconsiderTransitions.back(), considerNodes.back()); |
|||
} |
|||
|
|||
uint64_t tnextconsider = builder.addImmediateTransition(getFailPriority(dftSpare), 0.0, dftSpare->name() + "_cannot_claim_" + child->name()); |
|||
builder.setTransitionLayoutInfo(tnextconsider, storm::gspn::LayoutInfo(xcenter-7.0+j*14.0, ycenter-8.0)); |
|||
builder.addInputArc(considerNodes.back(), tnextconsider); |
|||
builder.addInputArc(unavailableNodes.at(child->id()), tnextconsider); |
|||
nextconsiderTransitions.push_back(tnextconsider); |
|||
|
|||
} |
|||
size_t nodeCUC = builder.addPlace(defaultCapacity, j == 0 ? 1 : 0, dftSpare->name() + "_claimed_" + child->name()); |
|||
cucNodes.push_back(nodeCUC); |
|||
builder.setPlaceLayoutInfo(nodeCUC, storm::gspn::LayoutInfo(xcenter-9.0+j*14.0, ycenter+5.0)); |
|||
if (j > 0) { |
|||
uint64 tclaim = builder.addImmediateTransition(getFailPriority(dftSpare), 0.0, dftSpare->name() + "_claim_" + child->name()); |
|||
builder.setTransitionLayoutInfo(tclaim, storm::gspn::LayoutInfo(xcenter-9.0+j*14.0, ycenter)); |
|||
builder.addInhibitionArc(unavailableNodes.at(child->id()), tclaim); |
|||
builder.addInputArc(considerNodes.back(), tclaim); |
|||
builder.addOutputArc(tclaim, cucNodes.back()); |
|||
} |
|||
uint64_t tnextcl = builder.addImmediateTransition(getFailPriority(dftSpare), 0.0, dftSpare->name() + "_next_claim_" + std::to_string(j)); |
|||
builder.setTransitionLayoutInfo(tnextcl, storm::gspn::LayoutInfo(xcenter-3.0+j*14.0, ycenter+5.0)); |
|||
builder.addInputArc(cucNodes.back(), tnextcl); |
|||
builder.addInputArc(failedNodes.at(child->id()), tnextcl); |
|||
builder.addOutputArc(tnextcl, failedNodes.at(child->id())); |
|||
nextclTransitions.push_back(tnextcl); |
|||
++j; |
|||
for (uint64_t k : mDft.module(child->id())) { |
|||
|
|||
uint64_t tactive = builder.addImmediateTransition(defaultPriority+1, 0.0, dftSpare->name() + "_activate_" + std::to_string(j) + "_" + std::to_string(k)); |
|||
builder.addInputArc(cucNodes.back(), tactive); |
|||
builder.addOutputArc(tactive, cucNodes.back()); |
|||
builder.addInputArc(spareActive, tactive); |
|||
builder.addOutputArc(tactive, activeNodes.at(k)); |
|||
builder.addInhibitionArc(activeNodes.at(k), tactive); |
|||
} |
|||
|
|||
|
|||
} |
|||
builder.addOutputArc(nextconsiderTransitions.back(), nodeFailed); |
|||
builder.addOutputArc(nextclTransitions.back(), nodeFailed); |
|||
|
|||
if (isRepresentative) { |
|||
builder.addOutputArc(nextconsiderTransitions.back(), unavailableNode); |
|||
builder.addOutputArc(nextclTransitions.back(), unavailableNode); |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawCONSTF(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dftConstF, bool isRepresentative) { |
|||
failedNodes.push_back(builder.addPlace(defaultCapacity, 1, dftConstF->name() + STR_FAILED)); |
|||
uint64_t unavailableNode = 0; |
|||
if (isRepresentative) { |
|||
// TODO set position
|
|||
unavailableNode = addUnavailableNode(dftConstF, storm::gspn::LayoutInfo(0, 0), false); |
|||
} |
|||
|
|||
} |
|||
//
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawCONSTS(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dftConstS, bool isRepresentative) { |
|||
// storm::gspn::Place placeCONSTSFailed;
|
|||
// placeCONSTSFailed.setName(dftConstS->name() + STR_FAILED);
|
|||
// placeCONSTSFailed.setNumberOfInitialTokens(0);
|
|||
// placeCONSTSFailed.setCapacity(0); // It cannot contain a token, because it cannot fail.
|
|||
// mGspn.addPlace(placeCONSTSFailed);
|
|||
} |
|||
//
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawPDEP(std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dftDependency) { |
|||
double xcenter = mDft.getElementLayoutInfo(dftDependency->id()).x;; |
|||
double ycenter = mDft.getElementLayoutInfo(dftDependency->id()).y;; |
|||
|
|||
uint64_t coinPlace = builder.addPlace(defaultCapacity, 1, dftDependency->name() + "_coin"); |
|||
builder.setPlaceLayoutInfo(coinPlace, storm::gspn::LayoutInfo(xcenter-5.0, ycenter+2.0)); |
|||
uint64_t t1 = builder.addImmediateTransition(defaultPriority, 0.0, dftDependency->name() + "_start_flip"); |
|||
|
|||
builder.addInputArc(coinPlace, t1); |
|||
builder.addInputArc(failedNodes.at(dftDependency->triggerEvent()->id()), t1); |
|||
builder.addOutputArc(t1, failedNodes.at(dftDependency->triggerEvent()->id())); |
|||
uint64_t forwardPlace = builder.addPlace(defaultCapacity, 0, dftDependency->name() + "_forward"); |
|||
builder.setPlaceLayoutInfo(forwardPlace, storm::gspn::LayoutInfo(xcenter+1.0, ycenter+2.0)); |
|||
|
|||
if (!smart || dftDependency->probability() < 1.0) { |
|||
uint64_t flipPlace = builder.addPlace(defaultCapacity, 0, dftDependency->name() + "_flip"); |
|||
builder.addOutputArc(t1, flipPlace); |
|||
|
|||
builder.setPlaceLayoutInfo(flipPlace, storm::gspn::LayoutInfo(xcenter-2.0, ycenter+2.0)); |
|||
uint64_t t2 = builder.addImmediateTransition(defaultPriority + 1, dftDependency->probability(), "_win_flip"); |
|||
builder.addInputArc(flipPlace, t2); |
|||
builder.addOutputArc(t2, forwardPlace); |
|||
if (dftDependency->probability() < 1.0) { |
|||
uint64_t t3 = builder.addImmediateTransition(defaultPriority + 1, 1 - dftDependency->probability(), "_loose_flip"); |
|||
builder.addInputArc(flipPlace, t3); |
|||
} |
|||
} else { |
|||
builder.addOutputArc(t1, forwardPlace); |
|||
} |
|||
for(auto const& depEv : dftDependency->dependentEvents()) { |
|||
uint64_t tx = builder.addImmediateTransition(defaultPriority, 0.0, dftDependency->name() + "_propagate_" + depEv->name()); |
|||
builder.addInputArc(forwardPlace, tx); |
|||
builder.addOutputArc(tx, forwardPlace); |
|||
builder.addOutputArc(tx, failedNodes.at(depEv->id())); |
|||
builder.addInhibitionArc(failedNodes.at(depEv->id()), tx); |
|||
if (!smart || depEv->nrRestrictions() > 0) { |
|||
builder.addInhibitionArc(disabledNodes.at(depEv->id()), tx); |
|||
} |
|||
if (!smart || mDft.isRepresentative(depEv->id())) { |
|||
builder.addOutputArc(tx, unavailableNodes.at(depEv->id())); |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawSeq(std::shared_ptr<storm::storage::DFTSeq<ValueType> const> dftSeq) { |
|||
STORM_LOG_THROW(dftSeq->allChildrenBEs(), storm::exceptions::NotImplementedException, "Sequence enforcers with gates as children are currently not supported"); |
|||
uint64_t j = 0; |
|||
uint64_t tEnable = 0; |
|||
uint64_t nextPlace = 0; |
|||
for(auto const& child : dftSeq->children()) { |
|||
nextPlace = builder.addPlace(defaultCapacity, j==0 ? 1 : 0, dftSeq->name() + "_next_" + child->name()); |
|||
if (j>0) { |
|||
builder.addOutputArc(tEnable, nextPlace); |
|||
} |
|||
tEnable = builder.addImmediateTransition(defaultPriority + 1, 0.0, dftSeq->name() + "_unblock_" +child->name() ); |
|||
builder.addInputArc(nextPlace, tEnable); |
|||
builder.addInputArc(disabledNodes.at(child->id()), tEnable); |
|||
if (j>0) { |
|||
builder.addInputArc(failedNodes.at(dftSeq->children().at(j-1)->id()), tEnable); |
|||
} |
|||
++j; |
|||
} |
|||
|
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t DftToGspnTransformator<ValueType>::addUnavailableNode(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dftElement, storm::gspn::LayoutInfo const& layoutInfo, bool initialAvailable) { |
|||
uint64_t unavailableNode = builder.addPlace(defaultCapacity, initialAvailable ? 0 : 1, dftElement->name() + "_unavailable"); |
|||
assert(unavailableNode != 0); |
|||
unavailableNodes.emplace(dftElement->id(), unavailableNode); |
|||
builder.setPlaceLayoutInfo(unavailableNode, layoutInfo); |
|||
return unavailableNode; |
|||
} |
|||
|
|||
template<typename ValueType> |
|||
uint64_t DftToGspnTransformator<ValueType>::addDisabledPlace(std::shared_ptr<const storm::storage::DFTBE<ValueType> > dftBe) { |
|||
uint64_t disabledNode = builder.addPlace(dftBe->nrRestrictions(), dftBe->nrRestrictions(), dftBe->name() + "_dabled"); |
|||
disabledNodes.emplace(dftBe->id(), disabledNode); |
|||
return disabledNode; |
|||
} |
|||
//
|
|||
|
|||
template <typename ValueType> |
|||
bool DftToGspnTransformator<ValueType>::isBEActive(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dftElement) |
|||
{ |
|||
// If element is the top element, return true.
|
|||
if (dftElement->id() == mDft.getTopLevelIndex()) { |
|||
return true; |
|||
} |
|||
else { // Else look at all parents.
|
|||
auto parents = dftElement->parents(); |
|||
std::vector<bool> pathValidities; |
|||
|
|||
for (std::size_t i = 0; i < parents.size(); i++) { |
|||
// Add all parents to the vector, except if the parent is a SPARE and the current element is an inactive child of the SPARE.
|
|||
if (parents[i]->type() == storm::storage::DFTElementType::SPARE) { |
|||
auto children = std::static_pointer_cast<storm::storage::DFTSpare<ValueType> const>(parents[i])->children(); |
|||
if (children[0]->id() != dftElement->id()) { |
|||
continue; |
|||
} |
|||
} |
|||
|
|||
pathValidities.push_back(isBEActive(parents[i])); |
|||
} |
|||
|
|||
// Check all vector entries. If one is true, a "valid" path has been found.
|
|||
for (std::size_t i = 0; i < pathValidities.size(); i++) { |
|||
if (pathValidities[i]) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// No "valid" path found. BE is inactive.
|
|||
return false; |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
uint64_t DftToGspnTransformator<ValueType>::getFailPriority(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dftElement) |
|||
{ |
|||
return mDft.maxRank() - dftElement->rank() + 2; |
|||
} |
|||
|
|||
|
|||
template <typename ValueType> |
|||
void DftToGspnTransformator<ValueType>::drawGSPNRestrictions() { |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
gspn::GSPN* DftToGspnTransformator<ValueType>::obtainGSPN() { |
|||
return builder.buildGspn(); |
|||
} |
|||
|
|||
// Explicitly instantiate the class.
|
|||
template class DftToGspnTransformator<double>; |
|||
|
|||
|
|||
#ifdef STORM_HAVE_CARL
|
|||
// template class DftToGspnTransformator<storm::RationalFunction>;
|
|||
#endif
|
|||
|
|||
} // namespace dft
|
|||
} // namespace transformations
|
|||
} // namespace storm
|
|||
|
|||
|
|||
@ -0,0 +1,163 @@ |
|||
#pragma once |
|||
|
|||
#include "storm-dft/storage/dft/DFT.h" |
|||
#include "storm-gspn/storage/gspn/GSPN.h" |
|||
#include "storm-gspn/storage/gspn/GspnBuilder.h" |
|||
|
|||
namespace storm { |
|||
namespace transformations { |
|||
namespace dft { |
|||
|
|||
/*! |
|||
* Transformator for DFT -> GSPN. |
|||
*/ |
|||
template<typename ValueType> |
|||
class DftToGspnTransformator { |
|||
|
|||
public: |
|||
/*! |
|||
* Constructor. |
|||
* |
|||
* @param dft DFT |
|||
*/ |
|||
DftToGspnTransformator(storm::storage::DFT<ValueType> const& dft); |
|||
|
|||
/*! |
|||
* Transform the DFT to a GSPN. |
|||
*/ |
|||
void transform(); |
|||
|
|||
/*! |
|||
* Extract Gspn by building |
|||
* |
|||
*/ |
|||
gspn::GSPN* obtainGSPN(); |
|||
|
|||
|
|||
uint64_t toplevelFailedPlaceId(); |
|||
|
|||
private: |
|||
/* |
|||
* Draw all elements of the GSPN. |
|||
*/ |
|||
void drawGSPNElements(); |
|||
/* |
|||
* Draw restrictions between the elements of the GSPN (i.e. SEQ or MUTEX). |
|||
*/ |
|||
void drawGSPNRestrictions(); |
|||
|
|||
/* |
|||
* Draw a Petri net Basic Event. |
|||
* |
|||
* @param dftBE The Basic Event. |
|||
*/ |
|||
void drawBE(std::shared_ptr<storm::storage::DFTBE<ValueType> const> dftBE, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net AND. |
|||
* |
|||
* @param dftAnd The AND gate. |
|||
*/ |
|||
void drawAND(std::shared_ptr<storm::storage::DFTAnd<ValueType> const> dftAnd, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net OR. |
|||
* |
|||
* @param dftOr The OR gate. |
|||
*/ |
|||
void drawOR(std::shared_ptr<storm::storage::DFTOr<ValueType> const> dftOr, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net VOT. |
|||
* |
|||
* @param dftVot The VOT gate. |
|||
*/ |
|||
void drawVOT(std::shared_ptr<storm::storage::DFTVot<ValueType> const> dftVot, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net PAND. |
|||
* This PAND is inklusive (children are allowed to fail simultaneously and the PAND will fail nevertheless). |
|||
* |
|||
* @param dftPand The PAND gate. |
|||
*/ |
|||
void drawPAND(std::shared_ptr<storm::storage::DFTPand<ValueType> const> dftPand, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net SPARE. |
|||
* |
|||
* @param dftSpare The SPARE gate. |
|||
*/ |
|||
void drawSPARE(std::shared_ptr<storm::storage::DFTSpare<ValueType> const> dftSpare, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net POR. |
|||
* This POR is inklusive (children are allowed to fail simultaneously and the POR will fail nevertheless). |
|||
* |
|||
* @param dftPor The POR gate. |
|||
*/ |
|||
void drawPOR(std::shared_ptr<storm::storage::DFTPor<ValueType> const> dftPor, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net CONSTF (Constant Failure, a Basic Event that has already failed). |
|||
* |
|||
* @param dftPor The CONSTF Basic Event. |
|||
*/ |
|||
void drawCONSTF(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dftConstF, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net CONSTS (Constant Save, a Basic Event that cannot fail). |
|||
* |
|||
* @param dftPor The CONSTS Basic Event. |
|||
*/ |
|||
void drawCONSTS(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dftConstS, bool isRepresentative); |
|||
|
|||
/* |
|||
* Draw a Petri net PDEP (FDEP is included with a firerate of 1). |
|||
*/ |
|||
void drawPDEP(std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dftDependency); |
|||
|
|||
|
|||
void drawSeq(std::shared_ptr<storm::storage::DFTSeq<ValueType> const> dftSeq); |
|||
/* |
|||
* Return true if BE is active (corresponding place contains one initial token) or false if BE is inactive (corresponding place contains no initial token). |
|||
* |
|||
* @param dFTElement DFT element. |
|||
*/ |
|||
bool isBEActive(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dFTElement); |
|||
|
|||
/* |
|||
* Get the priority of the element. |
|||
* The priority is two times the length of the shortest path to the top event. |
|||
* |
|||
* @param priority The priority of the gate. Top Event has priority 0, its children 2, its grandchildren 4, ... |
|||
* |
|||
* @param dftElement The element whose priority shall be determined. |
|||
*/ |
|||
uint64_t getFailPriority(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dFTElement); |
|||
|
|||
|
|||
uint64_t addUnavailableNode(std::shared_ptr<storm::storage::DFTElement<ValueType> const> dftElement, storm::gspn::LayoutInfo const& layoutInfo, bool initialAvailable = true); |
|||
|
|||
uint64_t addDisabledPlace(std::shared_ptr<storm::storage::DFTBE<ValueType> const> dftBe); |
|||
|
|||
storm::storage::DFT<ValueType> const& mDft; |
|||
storm::gspn::GspnBuilder builder; |
|||
std::vector<uint64_t> failedNodes; |
|||
std::map<uint64_t, uint64_t> unavailableNodes; |
|||
std::map<uint64_t, uint64_t> activeNodes; |
|||
std::map<uint64_t, uint64_t> disabledNodes; |
|||
bool smart = true; |
|||
|
|||
static constexpr const char* STR_FAILING = "_failing"; // Name standard for transitions that point towards a place, which in turn indicates the failure of a gate. |
|||
static constexpr const char* STR_FAILED = "_failed"; // Name standard for place which indicates the failure of a gate. |
|||
static constexpr const char* STR_FAILSAVING = "_failsaving"; // Name standard for transition that point towards a place, which in turn indicates the failsave state of a gate. |
|||
static constexpr const char* STR_FAILSAVE = "_failsave"; // Name standard for place which indicates the failsave state of a gate. |
|||
static constexpr const char* STR_ACTIVATED = "_activated"; // Name standard for place which indicates the activity. |
|||
static constexpr const char* STR_ACTIVATING = "_activating"; // Name standard for transition that point towards a place, which in turn indicates its activity. |
|||
|
|||
|
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,17 @@ |
|||
#pragma once |
|||
|
|||
namespace storm { |
|||
namespace gspn { |
|||
struct LayoutInfo { |
|||
LayoutInfo() {}; |
|||
LayoutInfo(double x, double y, double rotation = 0.0) : x(x), y(y), rotation(rotation) {}; |
|||
|
|||
// x location |
|||
double x = 0.0; |
|||
// y location |
|||
double y = 0.0; |
|||
// degrees |
|||
double rotation = 0.0; |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
#pragma once |
|||
#include <vector> |
|||
|
|||
namespace storm { |
|||
namespace gspn { |
|||
|
|||
struct TransitionPartition { |
|||
std::vector<uint64_t> transitions; |
|||
uint64_t priority; |
|||
|
|||
|
|||
uint64_t nrTransitions() const { |
|||
return transitions.size(); |
|||
} |
|||
}; |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
#pragma once |
|||
|
|||
#include "storm/storage/jani/Model.h" |
|||
|
|||
#include "storm-gspn/builder/JaniGSPNBuilder.h" |
|||
#include "storm-gspn/storage/gspn/GSPN.h" |
|||
|
|||
#include "storm/settings/SettingsManager.h" |
|||
#include "storm/settings/modules/GSPNExportSettings.h" |
|||
|
|||
namespace storm { |
|||
/** |
|||
* Builds JANI model from GSPN. |
|||
*/ |
|||
storm::jani::Model* buildJani(storm::gspn::GSPN const& gspn) { |
|||
std::shared_ptr<storm::expressions::ExpressionManager> exprManager(new storm::expressions::ExpressionManager()); |
|||
storm::builder::JaniGSPNBuilder builder(gspn, exprManager); |
|||
return builder.build(); |
|||
} |
|||
|
|||
void handleGSPNExportSettings(storm::gspn::GSPN const& gspn) { |
|||
storm::settings::modules::GSPNExportSettings const& exportSettings = storm::settings::getModule<storm::settings::modules::GSPNExportSettings>(); |
|||
if (exportSettings.isWriteToDotSet()) { |
|||
std::ofstream fs; |
|||
fs.open(exportSettings.getWriteToDotFilename()); |
|||
gspn.writeDotToStream(fs); |
|||
fs.close(); |
|||
} |
|||
|
|||
if (exportSettings.isWriteToPnproSet()) { |
|||
std::ofstream fs; |
|||
fs.open(exportSettings.getWriteToPnproFilename()); |
|||
gspn.toPnpro(fs); |
|||
fs.close(); |
|||
} |
|||
|
|||
if (exportSettings.isWriteToPnmlSet()) { |
|||
std::ofstream fs; |
|||
fs.open(exportSettings.getWriteToPnmlFilename()); |
|||
gspn.toPnml(fs); |
|||
fs.close(); |
|||
} |
|||
|
|||
if (exportSettings.isDisplayStatsSet()) { |
|||
std::cout << "============GSPN Statistics==============" << std::endl; |
|||
gspn.writeStatsToStream(std::cout); |
|||
std::cout << "=========================================" << std::endl; |
|||
} |
|||
|
|||
if (exportSettings.isWriteStatsToFileSet()) { |
|||
std::ofstream fs; |
|||
fs.open(exportSettings.getWriteStatsFilename()); |
|||
gspn.writeStatsToStream(fs); |
|||
fs.close(); |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
set(STORMPY_OUTPUT_DIR "${PROJECT_BINARY_DIR}/stormpy") |
|||
set(STORMPY_SOURCE_DIR "${PROJECT_SOURCE_DIR}/stormpy") |
|||
|
|||
if(STORM_HAVE_CLN) |
|||
set(STORMPY_USE_CLN 1) |
|||
else() |
|||
set(STORMPY_USE_CLN 0) |
|||
endif() |
|||
|
|||
# Set configuration file |
|||
get_directory_property(STORMPY_INCLUDE_DIRS_PROP INCLUDE_DIRECTORIES) |
|||
foreach(arg ${STORMPY_INCLUDE_DIRS_PROP}) |
|||
set(STORMPY_INCLUDE_DIRS "${STORMPY_INCLUDE_DIRS}${sep}${arg}") |
|||
set(sep ":") |
|||
endforeach() |
|||
set(STORMPY_COMPILE_ARGS ${CMAKE_CXX_FLAGS}) |
|||
set(STORMPY_LIBRARY_DIRS "${PROJECT_BINARY_DIR}/src/storm") |
|||
set(STORMPY_RPATH "${PROJECT_BINARY_DIR}/src/storm") |
|||
configure_file ( |
|||
"${PROJECT_SOURCE_DIR}/stormpy/setup.cfg.in" |
|||
"${PROJECT_SOURCE_DIR}/stormpy/setup.cfg" |
|||
) |
|||
|
|||
# Add targets |
|||
add_custom_target(PythonBindingsBuild |
|||
COMMAND ${PYTHON_EXECUTABLE} setup.py build |
|||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} |
|||
DEPENDS storm |
|||
) |
|||
add_custom_target(PythonBindingsDevelop |
|||
COMMAND ${PYTHON_EXECUTABLE} setup.py develop |
|||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} |
|||
DEPENDS storm |
|||
) |
|||
add_custom_target(PythonBindingsInstall |
|||
COMMAND ${PYTHON_EXECUTABLE} setup.py install --user |
|||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} |
|||
DEPENDS storm |
|||
) |
|||
@ -1 +0,0 @@ |
|||
recursive-include src *.h |
|||
@ -1,3 +0,0 @@ |
|||
*.so |
|||
__pycache__/ |
|||
stormpy.egg-info/ |
|||
@ -1,70 +0,0 @@ |
|||
""" |
|||
It looks like you want to know about 'stormpy'. |
|||
|
|||
_.-;:q=._ |
|||
.' j=""^k;:\. |
|||
; .F ";`Y |
|||
,;.J_ ;'j |
|||
,-;"^7F : .F _________________ |
|||
,-'-_<. ;gj. _.,---""'' .' |
|||
; _,._`\. : `T"5, ; |
|||
: `?8w7 `J ,-'" -^q. ` ; |
|||
\;._ _,=' ; n58L Y. .' |
|||
F;"; .' k_ `^' j' ; |
|||
J;:: ; "y:-=' ; |
|||
L;;== |:; jT\ ; |
|||
L;:;J J:L 7:;' _ ; |
|||
I;|:.L |:k J:.' , ' . ; |
|||
|;J:.| ;.I F.: . : |
|||
;J;:L:: |.| |.J , ' ` ; ; |
|||
.' J:`J.`. :.J |. L . ; ; |
|||
; L :k:`._ ,',j J; | ` , ; ; |
|||
.' I :`=.:."_".' L J `.' |
|||
.' |.: `"-=-' |.J ; |
|||
_.-' `: : ;:; _ ; |
|||
_.-'" J: : /.;' ; ; |
|||
='_ k;.\. _.;:Y' , .' |
|||
`"---..__ `Y;."-=';:=' , .' |
|||
`""--..__ `"==="' - .' |
|||
``""---...__ itz .-' |
|||
``""---' |
|||
""" |
|||
|
|||
from . import core |
|||
from .core import * |
|||
from . import storage |
|||
from .storage import * |
|||
|
|||
core.set_up("") |
|||
|
|||
def build_model(program, formulae): |
|||
intermediate = core._build_model(program, formulae) |
|||
assert not intermediate.supports_parameters |
|||
if intermediate.model_type == ModelType.DTMC: |
|||
return intermediate.as_dtmc() |
|||
elif intermediate.model_type == ModelType.MDP: |
|||
return intermediate.as_mdp() |
|||
else: |
|||
raise RuntimeError("Not supported non-parametric model constructed") |
|||
|
|||
def build_parametric_model(program, formulae): |
|||
intermediate = core._build_parametric_model(program, formulae) |
|||
assert intermediate.supports_parameters |
|||
if intermediate.model_type == ModelType.DTMC: |
|||
return intermediate.as_pdtmc() |
|||
elif intermediate.model_type == ModelType.MDP: |
|||
return intermediate.as_pmdp() |
|||
else: |
|||
raise RuntimeError("Not supported parametric model constructed") |
|||
|
|||
def perform_bisimulation(model, formula, bisimulation_type): |
|||
if model.supports_parameters: |
|||
return core._perform_parametric_bisimulation(model, formula, bisimulation_type) |
|||
else: |
|||
return core._perform_bisimulation(model, formula, bisimulation_type) |
|||
|
|||
def model_checking(model, formula): |
|||
if model.supports_parameters: |
|||
return core._parametric_model_checking(model, formula) |
|||
else: |
|||
return core._model_checking(model, formula) |
|||
@ -1,2 +0,0 @@ |
|||
from . import expressions |
|||
from .expressions import * |
|||
@ -1,2 +0,0 @@ |
|||
from . import info |
|||
from .info import * |
|||
@ -1,2 +0,0 @@ |
|||
from . import logic |
|||
from .logic import * |
|||
@ -1,3 +0,0 @@ |
|||
from . import storage |
|||
from .storage import * |
|||
from . import state,action |
|||
@ -1,36 +0,0 @@ |
|||
class Action: |
|||
""" Represents an action in the model """ |
|||
|
|||
def __init__(self, row_group_start, row_group_end, row, model): |
|||
""" Initialize |
|||
:param row_group_start: Start index of the row group in the matrix |
|||
:param row_group_end: End index of the row group in the matrix |
|||
:param row: Index of the corresponding row in the matrix |
|||
:param model: Corresponding model |
|||
""" |
|||
self.row_group_start = row_group_start |
|||
self.row_group_end = row_group_end |
|||
self.row = row - 1 |
|||
self.model = model |
|||
assert row >= -1 and row + row_group_start <= row_group_end |
|||
|
|||
def __iter__(self): |
|||
return self |
|||
|
|||
def __next__(self): |
|||
if self.row + self.row_group_start >= self.row_group_end - 1: |
|||
raise StopIteration |
|||
else: |
|||
self.row += 1 |
|||
return self |
|||
|
|||
def __str__(self): |
|||
return "{}".format(self.row) |
|||
|
|||
def transitions(self): |
|||
""" Get transitions associated with the action |
|||
:return List of tranistions |
|||
""" |
|||
row = self.row_group_start + self.row |
|||
#return self.model.transition_matrix().get_row(self.row_group_start + self.row) |
|||
return self.model.transition_matrix.row_iter(row, row) |
|||
@ -1,34 +0,0 @@ |
|||
from . import action |
|||
|
|||
class State: |
|||
""" Represents a state in the model """ |
|||
|
|||
def __init__(self, id, model): |
|||
""" Initialize |
|||
:param id: Id of the state |
|||
:param model: Corresponding model |
|||
""" |
|||
self.id = id - 1 |
|||
self.model = model |
|||
|
|||
def __iter__(self): |
|||
return self |
|||
|
|||
def __next__(self): |
|||
if self.id >= self.model.nr_states - 1: |
|||
raise StopIteration |
|||
else: |
|||
self.id += 1 |
|||
return self |
|||
|
|||
def __str__(self): |
|||
return "{}".format(self.id) |
|||
|
|||
def actions(self): |
|||
""" Get actions associated with the state |
|||
:return List of actions |
|||
""" |
|||
row_group_indices = self.model.transition_matrix._row_group_indices |
|||
start = row_group_indices[self.id] |
|||
end = row_group_indices[self.id+1] |
|||
return action.Action(start, end, 0, self.model) |
|||
@ -1,24 +0,0 @@ |
|||
version: 1.0.{build} |
|||
os: Visual Studio 2015 |
|||
clone_folder: C:\projects\pybind11 |
|||
test: off |
|||
configuration: |
|||
- Release |
|||
- Debug |
|||
branches: |
|||
only: |
|||
- master |
|||
environment: |
|||
matrix: |
|||
- CMAKE_PLATFORM: "Visual Studio 14 2015" |
|||
PYTHON_DIR: "C:\\Python34" |
|||
- CMAKE_PLATFORM: "Visual Studio 14 2015 Win64" |
|||
PYTHON_DIR: "C:\\Python34-x64" |
|||
install: |
|||
- cinstall: python |
|||
build_script: |
|||
- echo Running cmake... |
|||
- cd c:\projects\pybind11 |
|||
- cmake -G "%CMAKE_PLATFORM%" -DPYTHON_EXECUTABLE:FILEPATH=%PYTHON_DIR%/python.exe |
|||
- set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" |
|||
- cmake --build . --config %Configuration% --target check -- /v:m /logger:%MSBuildLogger% |
|||
@ -1,33 +0,0 @@ |
|||
CMakeCache.txt |
|||
CMakeFiles |
|||
Makefile |
|||
cmake_install.cmake |
|||
.DS_Store |
|||
/example/example.so |
|||
/example/example.cpython*.so |
|||
/example/example.pyd |
|||
/example/example*.dll |
|||
*.sln |
|||
*.sdf |
|||
*.opensdf |
|||
*.vcxproj |
|||
*.filters |
|||
example.dir |
|||
Win32 |
|||
x64 |
|||
Release |
|||
Debug |
|||
.vs |
|||
CTestTestfile.cmake |
|||
Testing |
|||
autogen |
|||
MANIFEST |
|||
/.ninja_* |
|||
/*.ninja |
|||
/docs/.build |
|||
*.py[co] |
|||
*.egg-info |
|||
*~ |
|||
.DS_Store |
|||
/dist |
|||
/build |
|||
@ -1,3 +0,0 @@ |
|||
[submodule "tools/clang"] |
|||
path = tools/clang |
|||
url = https://github.com/wjakob/clang-cindex-python3 |
|||
@ -1,44 +0,0 @@ |
|||
language: cpp |
|||
sudo: false |
|||
cache: |
|||
directories: |
|||
- $HOME/.cache/pip |
|||
- ccache |
|||
addons: |
|||
apt: |
|||
sources: |
|||
- ubuntu-toolchain-r-test |
|||
- deadsnakes |
|||
- kubuntu-backports # cmake 2.8.12 |
|||
packages: |
|||
- g++-4.8 |
|||
- g++-4.8-multilib |
|||
- g++-multilib |
|||
- python3.5 |
|||
- python3.5-dev |
|||
- python3.5-venv |
|||
- python3.5-dev:i386 |
|||
- cmake |
|||
matrix: |
|||
include: |
|||
- os: linux |
|||
compiler: gcc-4.8 |
|||
install: |
|||
- pyvenv-3.5 venv |
|||
- source venv/bin/activate |
|||
- pip install -U pip wheel |
|||
- pip install numpy |
|||
script: |
|||
- CXX=g++-4.8 cmake -DPYBIND11_PYTHON_VERSION=3.5 |
|||
- CTEST_OUTPUT_ON_FAILURE=TRUE make check -j 2 |
|||
- os: osx |
|||
compiler: clang |
|||
script: |
|||
- cmake -DPYBIND11_PYTHON_VERSION=2.7 |
|||
- CTEST_OUTPUT_ON_FAILURE=TRUE make check -j 2 |
|||
#- os: linux |
|||
#compiler: gcc-4.8 |
|||
#script: |
|||
#- pyvenv-3.5 venv |
|||
#- cmake -DPYBIND11_PYTHON_VERSION=3.5 -DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.5m -DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.5m.so -DPYTHON_EXECUTABLE:FILEPATH=`pwd`/venv/bin/python3.5 -DCMAKE_CXX_COMPILER=g++-4.8 -DCMAKE_CXX_FLAGS=-m32 |
|||
#- make -j 2 |
|||
@ -1,170 +0,0 @@ |
|||
# CMakeLists.txt -- Build system for the pybind11 examples |
|||
# |
|||
# Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch> |
|||
# |
|||
# All rights reserved. Use of this source code is governed by a |
|||
# BSD-style license that can be found in the LICENSE file. |
|||
|
|||
cmake_minimum_required(VERSION 2.8.12) |
|||
|
|||
project(pybind11) |
|||
|
|||
# Check if pybind11 is being used directly or via add_subdirectory |
|||
set(PYBIND11_MASTER_PROJECT OFF) |
|||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) |
|||
set(PYBIND11_MASTER_PROJECT ON) |
|||
endif() |
|||
|
|||
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) |
|||
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) |
|||
|
|||
# Add a CMake parameter for choosing a desired Python version |
|||
set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling the example application") |
|||
|
|||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/tools") |
|||
set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6 3.7) |
|||
find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) |
|||
|
|||
include(CheckCXXCompilerFlag) |
|||
|
|||
if(NOT MSVC AND NOT PYBIND11_CPP_STANDARD) |
|||
check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) |
|||
check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) |
|||
|
|||
if (HAS_CPP14_FLAG) |
|||
set(PYBIND11_CPP_STANDARD -std=c++14) |
|||
elseif (HAS_CPP11_FLAG) |
|||
set(PYBIND11_CPP_STANDARD -std=c++11) |
|||
else() |
|||
message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") |
|||
endif() |
|||
|
|||
set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING |
|||
"C++ standard flag, e.g. -std=c++11 or -std=c++14. Defaults to latest available.") |
|||
endif() |
|||
|
|||
# Cache variables so pybind11_add_module can be used in parent projects |
|||
set(PYBIND11_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include" CACHE INTERNAL "") |
|||
set(PYTHON_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} CACHE INTERNAL "") |
|||
set(PYTHON_LIBRARIES ${PYTHON_LIBRARIES} CACHE INTERNAL "") |
|||
set(PYTHON_MODULE_PREFIX ${PYTHON_MODULE_PREFIX} CACHE INTERNAL "") |
|||
set(PYTHON_MODULE_EXTENSION ${PYTHON_MODULE_EXTENSION} CACHE INTERNAL "") |
|||
|
|||
# Build a Python extension module: |
|||
# pybind11_add_module(<name> source1 [source2 ...]) |
|||
# |
|||
function(pybind11_add_module target_name) |
|||
add_library(${target_name} MODULE ${ARGN}) |
|||
target_include_directories(${target_name} PUBLIC ${PYBIND11_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS}) |
|||
|
|||
# The prefix and extension are provided by FindPythonLibsNew.cmake |
|||
set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") |
|||
set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") |
|||
|
|||
if(WIN32 OR CYGWIN) |
|||
# Link against the Python shared library on Windows |
|||
target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) |
|||
elseif(APPLE) |
|||
# It's quite common to have multiple copies of the same Python version |
|||
# installed on one's system. E.g.: one copy from the OS and another copy |
|||
# that's statically linked into an application like Blender or Maya. |
|||
# If we link our plugin library against the OS Python here and import it |
|||
# into Blender or Maya later on, this will cause segfaults when multiple |
|||
# conflicting Python instances are active at the same time (even when they |
|||
# are of the same version). |
|||
|
|||
# Windows is not affected by this issue since it handles DLL imports |
|||
# differently. The solution for Linux and Mac OS is simple: we just don't |
|||
# link against the Python library. The resulting shared library will have |
|||
# missing symbols, but that's perfectly fine -- they will be resolved at |
|||
# import time. |
|||
|
|||
target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") |
|||
endif() |
|||
|
|||
if(NOT MSVC) |
|||
# Make sure C++11/14 are enabled |
|||
target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) |
|||
|
|||
# Enable link time optimization and set the default symbol |
|||
# visibility to hidden (very important to obtain small binaries) |
|||
string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) |
|||
if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) |
|||
# Check for Link Time Optimization support (GCC/Clang) |
|||
check_cxx_compiler_flag("-flto" HAS_LTO_FLAG) |
|||
if(HAS_LTO_FLAG AND NOT CYGWIN) |
|||
target_compile_options(${target_name} PRIVATE -flto) |
|||
endif() |
|||
|
|||
# Intel equivalent to LTO is called IPO |
|||
if(CMAKE_CXX_COMPILER_ID MATCHES "Intel") |
|||
check_cxx_compiler_flag("-ipo" HAS_IPO_FLAG) |
|||
if(HAS_IPO_FLAG) |
|||
target_compile_options(${target_name} PRIVATE -ipo) |
|||
endif() |
|||
endif() |
|||
|
|||
# Default symbol visibility |
|||
target_compile_options(${target_name} PRIVATE "-fvisibility=hidden") |
|||
|
|||
# Strip unnecessary sections of the binary on Linux/Mac OS |
|||
if(CMAKE_STRIP) |
|||
if(APPLE) |
|||
add_custom_command(TARGET ${target_name} POST_BUILD |
|||
COMMAND ${CMAKE_STRIP} -u -r $<TARGET_FILE:${target_name}>) |
|||
else() |
|||
add_custom_command(TARGET ${target_name} POST_BUILD |
|||
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:${target_name}>) |
|||
endif() |
|||
endif() |
|||
endif() |
|||
elseif(MSVC) |
|||
# /MP enables multithreaded builds (relevant when there are many files), /bigobj is |
|||
# needed for bigger binding projects due to the limit to 64k addressable sections |
|||
target_compile_options(${target_name} PRIVATE /MP /bigobj) |
|||
|
|||
# Enforce link time code generation on MSVC, except in debug mode |
|||
target_compile_options(${target_name} PRIVATE $<$<NOT:$<CONFIG:Debug>>:/GL>) |
|||
|
|||
# Fancy generator expressions don't work with linker flags, for reasons unknown |
|||
set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE /LTCG) |
|||
set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL /LTCG) |
|||
set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO /LTCG) |
|||
endif() |
|||
endfunction() |
|||
|
|||
# Compile with compiler warnings turned on |
|||
function(pybind11_enable_warnings target_name) |
|||
if(MSVC) |
|||
target_compile_options(${target_name} PRIVATE /W4) |
|||
else() |
|||
target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion) |
|||
endif() |
|||
endfunction() |
|||
|
|||
if (PYBIND11_TEST) |
|||
enable_testing() |
|||
add_subdirectory(example) |
|||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> DEPENDS example) |
|||
endif() |
|||
|
|||
if (PYBIND11_INSTALL) |
|||
set(PYBIND11_HEADERS |
|||
include/pybind11/attr.h |
|||
include/pybind11/cast.h |
|||
include/pybind11/common.h |
|||
include/pybind11/complex.h |
|||
include/pybind11/descr.h |
|||
include/pybind11/eigen.h |
|||
include/pybind11/functional.h |
|||
include/pybind11/numpy.h |
|||
include/pybind11/operators.h |
|||
include/pybind11/pybind11.h |
|||
include/pybind11/pytypes.h |
|||
include/pybind11/stl.h |
|||
include/pybind11/stl_bind.h |
|||
include/pybind11/typeid.h |
|||
) |
|||
|
|||
install(FILES ${PYBIND11_HEADERS} DESTINATION include/pybind11) |
|||
endif() |
|||
@ -1,37 +0,0 @@ |
|||
Thank you for your interest in this project! Please refer to the following |
|||
sections on how to contribute code and bug reports. |
|||
|
|||
### Reporting bugs |
|||
|
|||
At the moment, this project is run in the spare time of a single person |
|||
([Wenzel Jakob](http://rgl.epfl.ch/people/wjakob)) with very limited resources |
|||
for issue tracker tickets. Thus, before submitting a question or bug report, |
|||
please take a moment of your time and ensure that your issue isn't already |
|||
discussed in the project documentation provided at |
|||
[http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest). |
|||
|
|||
Assuming that you have identified a previously unknown problem or an important |
|||
question, it's essential that you submit a self-contained and minimal piece of |
|||
code that reproduces the problem. In other words: no external dependencies, |
|||
isolate the function(s) that cause breakage, submit matched and complete C++ |
|||
and Python snippets that can be easily compiled and run on my end. |
|||
|
|||
## Pull requests |
|||
Contributions are submitted, reviewed, and accepted using Github pull requests. |
|||
Please refer to [this |
|||
article](https://help.github.com/articles/using-pull-requests) for details and |
|||
adhere to the following rules to make the process as smooth as possible: |
|||
|
|||
* Make a new branch for every feature you're working on. |
|||
* Make small and clean pull requests that are easy to review but make sure they |
|||
do add value by themselves. |
|||
* Add tests for any new functionality and run the test suite (``make test``) to |
|||
ensure that no existing features break. |
|||
* This project has a strong focus on providing general solutions using a |
|||
minimal amount of code, thus small pull requests are greatly preferred. |
|||
|
|||
### License |
|||
|
|||
pybind11 is provided under a BSD-style license that can be found in the |
|||
``LICENSE`` file. By using, distributing, or contributing to this project, you |
|||
agree to the terms and conditions of this license. |
|||
@ -1,36 +0,0 @@ |
|||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions are met: |
|||
|
|||
1. Redistributions of source code must retain the above copyright notice, this |
|||
list of conditions and the following disclaimer. |
|||
|
|||
2. Redistributions in binary form must reproduce the above copyright notice, |
|||
this list of conditions and the following disclaimer in the documentation |
|||
and/or other materials provided with the distribution. |
|||
|
|||
3. Neither the name of the copyright holder nor the names of its contributors |
|||
may be used to endorse or promote products derived from this software |
|||
without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
|||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
|
|||
You are under no obligation whatsoever to provide any bug fixes, patches, or |
|||
upgrades to the features, functionality or performance of the source code |
|||
("Enhancements") to anyone; however, if you choose to make your Enhancements |
|||
available either publicly, or directly to the author of this software, without |
|||
imposing a separate written license agreement for such Enhancements, then you |
|||
hereby grant the following license: a non-exclusive, royalty-free perpetual |
|||
license to install, use, modify, prepare derivative works, incorporate into |
|||
other computer software, distribute, and sublicense such enhancements or |
|||
derivative works thereof, in binary and source code form. |
|||
@ -1 +0,0 @@ |
|||
include include/pybind11/*.h |
|||
@ -1,114 +0,0 @@ |
|||
 |
|||
|
|||
# pybind11 — Seamless operability between C++11 and Python |
|||
|
|||
[](http://pybind11.readthedocs.org/en/latest/?badge=latest) |
|||
[](https://travis-ci.org/pybind/pybind11) |
|||
[](https://ci.appveyor.com/project/wjakob/pybind11) |
|||
|
|||
**pybind11** is a lightweight header-only library that exposes C++ types in Python |
|||
and vice versa, mainly to create Python bindings of existing C++ code. Its |
|||
goals and syntax are similar to the excellent |
|||
[Boost.Python](http://www.boost.org/doc/libs/1_58_0/libs/python/doc/) library |
|||
by David Abrahams: to minimize boilerplate code in traditional extension |
|||
modules by inferring type information using compile-time introspection. |
|||
|
|||
The main issue with Boost.Python—and the reason for creating such a similar |
|||
project—is Boost. Boost is an enormously large and complex suite of utility |
|||
libraries that works with almost every C++ compiler in existence. This |
|||
compatibility has its cost: arcane template tricks and workarounds are |
|||
necessary to support the oldest and buggiest of compiler specimens. Now that |
|||
C++11-compatible compilers are widely available, this heavy machinery has |
|||
become an excessively large and unnecessary dependency. |
|||
|
|||
Think of this library as a tiny self-contained version of Boost.Python with |
|||
everything stripped away that isn't relevant for binding generation. Without |
|||
comments, the core header files only require ~2.5K lines of code and depend on |
|||
Python (2.7 or 3.x) and the C++ standard library. This compact implementation |
|||
was possible thanks to some of the new C++11 language features (specifically: |
|||
tuples, lambda functions and variadic templates). Since its creation, this |
|||
library has grown beyond Boost.Python in many ways, leading to dramatically |
|||
simpler binding code in many common situations. |
|||
|
|||
Tutorial and reference documentation is provided at |
|||
[http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest). |
|||
A PDF version of the manual is available |
|||
[here](https://media.readthedocs.org/pdf/pybind11/latest/pybind11.pdf). |
|||
|
|||
## Core features |
|||
pybind11 can map the following core C++ features to Python |
|||
|
|||
- Functions accepting and returning custom data structures per value, reference, or pointer |
|||
- Instance methods and static methods |
|||
- Overloaded functions |
|||
- Instance attributes and static attributes |
|||
- Exceptions |
|||
- Enumerations |
|||
- Callbacks |
|||
- Custom operators |
|||
- STL data structures |
|||
- Iterators and ranges |
|||
- Smart pointers with reference counting like `std::shared_ptr` |
|||
- Internal references with correct reference counting |
|||
- C++ classes with virtual (and pure virtual) methods can be extended in Python |
|||
|
|||
## Goodies |
|||
In addition to the core functionality, pybind11 provides some extra goodies: |
|||
|
|||
- pybind11 uses C++11 move constructors and move assignment operators whenever |
|||
possible to efficiently transfer custom data types. |
|||
|
|||
- It is possible to bind C++11 lambda functions with captured variables. The |
|||
lambda capture data is stored inside the resulting Python function object. |
|||
|
|||
- It's easy to expose the internal storage of custom data types through |
|||
Pythons' buffer protocols. This is handy e.g. for fast conversion between |
|||
C++ matrix classes like Eigen and NumPy without expensive copy operations. |
|||
|
|||
- pybind11 can automatically vectorize functions so that they are transparently |
|||
applied to all entries of one or more NumPy array arguments. |
|||
|
|||
- Python's slice-based access and assignment operations can be supported with |
|||
just a few lines of code. |
|||
|
|||
- Everything is contained in just a few header files; there is no need to link |
|||
against any additional libraries. |
|||
|
|||
- Binaries are generally smaller by a factor of 2 or more compared to |
|||
equivalent bindings generated by Boost.Python. |
|||
|
|||
- When supported by the compiler, two new C++14 features (relaxed constexpr and |
|||
return value deduction) are used to precompute function signatures at compile |
|||
time, leading to smaller binaries. |
|||
|
|||
- With little extra effort, C++ types can be pickled and unpickled similar to |
|||
regular Python objects. |
|||
|
|||
## Supported compilers |
|||
|
|||
1. Clang/LLVM (any non-ancient version with C++11 support) |
|||
2. GCC (any non-ancient version with C++11 support) |
|||
3. Microsoft Visual Studio 2015 or newer |
|||
4. Intel C++ compiler v15 or newer |
|||
5. Cygwin/GCC (tested on 2.5.1) |
|||
|
|||
## About |
|||
|
|||
This project was created by [Wenzel Jakob](https://www.mitsuba-renderer.org/~wenzel/). |
|||
Significant features and/or improvements to the code were contributed by |
|||
Jonas Adler, |
|||
Sylvain Corlay, |
|||
Axel Huebl, |
|||
@hulucc, |
|||
Sergey Lyskov |
|||
Johan Mabille, |
|||
Tomasz Miąsko, |
|||
Dean Moldovan, |
|||
Ben Pritchard, and |
|||
Boris Schäling. |
|||
|
|||
### License |
|||
|
|||
pybind11 is provided under a BSD-style license that can be found in the |
|||
``LICENSE`` file. By using, distributing, or contributing to this project, |
|||
you agree to the terms and conditions of this license. |
|||
@ -1,11 +0,0 @@ |
|||
.wy-table-responsive table td, |
|||
.wy-table-responsive table th { |
|||
white-space: initial !important; |
|||
} |
|||
.rst-content table.docutils td { |
|||
vertical-align: top !important; |
|||
} |
|||
div[class^='highlight'] pre { |
|||
white-space: pre; |
|||
white-space: pre-wrap; |
|||
} |
|||
1613
stormpy/resources/pybind11/docs/advanced.rst
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,309 +0,0 @@ |
|||
.. _basics: |
|||
|
|||
First steps |
|||
########### |
|||
|
|||
This sections demonstrates the basic features of pybind11. Before getting |
|||
started, make sure that development environment is set up to compile the |
|||
included set of examples, which also double as test cases. |
|||
|
|||
|
|||
Compiling the test cases |
|||
======================== |
|||
|
|||
Linux/MacOS |
|||
----------- |
|||
|
|||
On Linux you'll need to install the **python-dev** or **python3-dev** packages as |
|||
well as **cmake**. On Mac OS, the included python version works out of the box, |
|||
but **cmake** must still be installed. |
|||
|
|||
After installing the prerequisites, run |
|||
|
|||
.. code-block:: bash |
|||
|
|||
cmake . |
|||
make -j 4 |
|||
|
|||
followed by |
|||
|
|||
.. code-block:: bash |
|||
|
|||
make test |
|||
|
|||
Windows |
|||
------- |
|||
|
|||
On Windows, use the `CMake GUI`_ to create a Visual Studio project. Note that |
|||
only the 2015 release and newer versions are supported since pybind11 relies on |
|||
various C++11 language features that break older versions of Visual Studio. |
|||
After running CMake, open the created :file:`pybind11.sln` file and perform a |
|||
release build, which will will produce a file named |
|||
:file:`Release\\example.pyd`. Copy this file to the :file:`example` directory |
|||
and run :file:`example\\run_test.py` using the targeted Python version. |
|||
|
|||
.. _`CMake GUI`: https://cmake.org/runningcmake |
|||
|
|||
.. Note:: |
|||
|
|||
When all tests fail, make sure that |
|||
|
|||
1. The Python binary and the testcases are compiled for the same processor |
|||
type and bitness (i.e. either **i386** or **x86_64**) |
|||
|
|||
2. The Python binary used to run :file:`example\\run_test.py` matches the |
|||
Python version specified in the CMake GUI. This is controlled via |
|||
the ``PYTHON_EXECUTABLE`` ``PYTHON_INCLUDE_DIR``, and |
|||
``PYTHON_LIBRARY`` variables. |
|||
|
|||
.. seealso:: |
|||
|
|||
Advanced users who are already familiar with Boost.Python may want to skip |
|||
the tutorial and look at the test cases in the :file:`example` directory, |
|||
which exercise all features of pybind11. |
|||
|
|||
Creating bindings for a simple function |
|||
======================================= |
|||
|
|||
Let's start by creating Python bindings for an extremely simple function, which |
|||
adds two numbers and returns their result: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
int add(int i, int j) { |
|||
return i + j; |
|||
} |
|||
|
|||
For simplicity [#f1]_, we'll put both this function and the binding code into |
|||
a file named :file:`example.cpp` with the following contents: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
#include <pybind11/pybind11.h> |
|||
|
|||
int add(int i, int j) { |
|||
return i + j; |
|||
} |
|||
|
|||
namespace py = pybind11; |
|||
|
|||
PYBIND11_PLUGIN(example) { |
|||
py::module m("example", "pybind11 example plugin"); |
|||
|
|||
m.def("add", &add, "A function which adds two numbers"); |
|||
|
|||
return m.ptr(); |
|||
} |
|||
|
|||
The :func:`PYBIND11_PLUGIN` macro creates a function that will be called when an |
|||
``import`` statement is issued from within Python. The next line creates a |
|||
module named ``example`` (with the supplied docstring). The method |
|||
:func:`module::def` generates binding code that exposes the |
|||
``add()`` function to Python. The last line returns the internal Python object |
|||
associated with ``m`` to the Python interpreter. |
|||
|
|||
.. note:: |
|||
|
|||
Notice how little code was needed to expose our function to Python: all |
|||
details regarding the function's parameters and return value were |
|||
automatically inferred using template metaprogramming. This overall |
|||
approach and the used syntax are borrowed from Boost.Python, though the |
|||
underlying implementation is very different. |
|||
|
|||
pybind11 is a header-only-library, hence it is not necessary to link against |
|||
any special libraries (other than Python itself). On Windows, use the CMake |
|||
build file discussed in section :ref:`cmake`. On Linux and Mac OS, the above |
|||
example can be compiled using the following command |
|||
|
|||
.. code-block:: bash |
|||
|
|||
$ c++ -O3 -shared -std=c++11 -I <path-to-pybind11>/include `python-config --cflags --ldflags` example.cpp -o example.so |
|||
|
|||
In general, it is advisable to include several additional build parameters |
|||
that can considerably reduce the size of the created binary. Refer to section |
|||
:ref:`cmake` for a detailed example of a suitable cross-platform CMake-based |
|||
build system. |
|||
|
|||
Assuming that the created file :file:`example.so` (:file:`example.pyd` on Windows) |
|||
is located in the current directory, the following interactive Python session |
|||
shows how to load and execute the example. |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
$ python |
|||
Python 2.7.10 (default, Aug 22 2015, 20:33:39) |
|||
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin |
|||
Type "help", "copyright", "credits" or "license" for more information. |
|||
>>> import example |
|||
>>> example.add(1, 2) |
|||
3L |
|||
>>> |
|||
|
|||
.. _keyword_args: |
|||
|
|||
Keyword arguments |
|||
================= |
|||
|
|||
With a simple modification code, it is possible to inform Python about the |
|||
names of the arguments ("i" and "j" in this case). |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
m.def("add", &add, "A function which adds two numbers", |
|||
py::arg("i"), py::arg("j")); |
|||
|
|||
:class:`arg` is one of several special tag classes which can be used to pass |
|||
metadata into :func:`module::def`. With this modified binding code, we can now |
|||
call the function using keyword arguments, which is a more readable alternative |
|||
particularly for functions taking many parameters: |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> import example |
|||
>>> example.add(i=1, j=2) |
|||
3L |
|||
|
|||
The keyword names also appear in the function signatures within the documentation. |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> help(example) |
|||
|
|||
.... |
|||
|
|||
FUNCTIONS |
|||
add(...) |
|||
Signature : (i: int, j: int) -> int |
|||
|
|||
A function which adds two numbers |
|||
|
|||
A shorter notation for named arguments is also available: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
// regular notation |
|||
m.def("add1", &add, py::arg("i"), py::arg("j")); |
|||
// shorthand |
|||
using namespace pybind11::literals; |
|||
m.def("add2", &add, "i"_a, "j"_a); |
|||
|
|||
The :var:`_a` suffix forms a C++11 literal which is equivalent to :class:`arg`. |
|||
Note that the literal operator must first be made visible with the directive |
|||
``using namespace pybind11::literals``. This does not bring in anything else |
|||
from the ``pybind11`` namespace except for literals. |
|||
|
|||
.. _default_args: |
|||
|
|||
Default arguments |
|||
================= |
|||
|
|||
Suppose now that the function to be bound has default arguments, e.g.: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
int add(int i = 1, int j = 2) { |
|||
return i + j; |
|||
} |
|||
|
|||
Unfortunately, pybind11 cannot automatically extract these parameters, since they |
|||
are not part of the function's type information. However, they are simple to specify |
|||
using an extension of :class:`arg`: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
m.def("add", &add, "A function which adds two numbers", |
|||
py::arg("i") = 1, py::arg("j") = 2); |
|||
|
|||
The default values also appear within the documentation. |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> help(example) |
|||
|
|||
.... |
|||
|
|||
FUNCTIONS |
|||
add(...) |
|||
Signature : (i: int = 1, j: int = 2) -> int |
|||
|
|||
A function which adds two numbers |
|||
|
|||
The shorthand notation is also available for default arguments: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
// regular notation |
|||
m.def("add1", &add, py::arg("i") = 1, py::arg("j") = 2); |
|||
// shorthand |
|||
m.def("add2", &add, "i"_a=1, "j"_a=2); |
|||
|
|||
.. _supported_types: |
|||
|
|||
Supported data types |
|||
==================== |
|||
|
|||
The following basic data types are supported out of the box (some may require |
|||
an additional extension header to be included). To pass other data structures |
|||
as arguments and return values, refer to the section on binding :ref:`classes`. |
|||
|
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| Data type | Description | Header file | |
|||
+=================================+==========================+===============================+ |
|||
| ``int8_t``, ``uint8_t`` | 8-bit integers | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``int16_t``, ``uint16_t`` | 16-bit integers | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``int32_t``, ``uint32_t`` | 32-bit integers | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``char`` | Character literal | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``wchar_t`` | Wide character literal | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``const char *`` | UTF-8 string literal | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``const wchar_t *`` | Wide string literal | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::string`` | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::pair<T1, T2>`` | Pair of two custom types | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::reference_wrapper<...>`` | Reference type wrapper | :file:`pybind11/pybind11.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::complex<T>`` | Complex numbers | :file:`pybind11/complex.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::array<T, Size>`` | STL static array | :file:`pybind11/stl.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::vector<T>`` | STL dynamic array | :file:`pybind11/stl.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::list<T>`` | STL linked list | :file:`pybind11/stl.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::map<T1, T2>`` | STL ordered map | :file:`pybind11/stl.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::unordered_map<T1, T2>`` | STL unordered map | :file:`pybind11/stl.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::set<T>`` | STL ordered set | :file:`pybind11/stl.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::unordered_set<T>`` | STL unordered set | :file:`pybind11/stl.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``Eigen::Matrix<...>`` | Dense Eigen matrices | :file:`pybind11/eigen.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
| ``Eigen::SparseMatrix<...>`` | Sparse Eigen matrices | :file:`pybind11/eigen.h` | |
|||
+---------------------------------+--------------------------+-------------------------------+ |
|||
|
|||
|
|||
.. [#f1] In practice, implementation and binding code will generally be located |
|||
in separate files. |
|||
@ -1,90 +0,0 @@ |
|||
import random |
|||
import os |
|||
import time |
|||
import datetime as dt |
|||
|
|||
nfns = 4 # Functions per class |
|||
nargs = 4 # Arguments per function |
|||
|
|||
|
|||
def generate_dummy_code_pybind11(nclasses=10): |
|||
decl = "" |
|||
bindings = "" |
|||
|
|||
for cl in range(nclasses): |
|||
decl += "class cl%03i;\n" % cl |
|||
decl += '\n' |
|||
|
|||
for cl in range(nclasses): |
|||
decl += "class cl%03i {\n" % cl |
|||
decl += "public:\n" |
|||
bindings += ' py::class_<cl%03i>(m, "cl%03i")\n' % (cl, cl) |
|||
for fn in range(nfns): |
|||
ret = random.randint(0, nclasses - 1) |
|||
params = [random.randint(0, nclasses - 1) for i in range(nargs)] |
|||
decl += " cl%03i *fn_%03i(" % (ret, fn) |
|||
decl += ", ".join("cl%03i *" % p for p in params) |
|||
decl += ");\n" |
|||
bindings += ' .def("fn_%03i", &cl%03i::fn_%03i)\n' % \ |
|||
(fn, cl, fn) |
|||
decl += "};\n\n" |
|||
bindings += ' ;\n' |
|||
|
|||
result = "#include <pybind11/pybind11.h>\n\n" |
|||
result += "namespace py = pybind11;\n\n" |
|||
result += decl + '\n' |
|||
result += "PYBIND11_PLUGIN(example) {\n" |
|||
result += " py::module m(\"example\");" |
|||
result += bindings |
|||
result += " return m.ptr();" |
|||
result += "}" |
|||
return result |
|||
|
|||
|
|||
def generate_dummy_code_boost(nclasses=10): |
|||
decl = "" |
|||
bindings = "" |
|||
|
|||
for cl in range(nclasses): |
|||
decl += "class cl%03i;\n" % cl |
|||
decl += '\n' |
|||
|
|||
for cl in range(nclasses): |
|||
decl += "class cl%03i {\n" % cl |
|||
decl += "public:\n" |
|||
bindings += ' py::class_<cl%03i>("cl%03i")\n' % (cl, cl) |
|||
for fn in range(nfns): |
|||
ret = random.randint(0, nclasses - 1) |
|||
params = [random.randint(0, nclasses - 1) for i in range(nargs)] |
|||
decl += " cl%03i *fn_%03i(" % (ret, fn) |
|||
decl += ", ".join("cl%03i *" % p for p in params) |
|||
decl += ");\n" |
|||
bindings += ' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy<py::manage_new_object>())\n' % \ |
|||
(fn, cl, fn) |
|||
decl += "};\n\n" |
|||
bindings += ' ;\n' |
|||
|
|||
result = "#include <boost/python.hpp>\n\n" |
|||
result += "namespace py = boost::python;\n\n" |
|||
result += decl + '\n' |
|||
result += "BOOST_PYTHON_MODULE(example) {\n" |
|||
result += bindings |
|||
result += "}" |
|||
return result |
|||
|
|||
|
|||
for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: |
|||
print ("{") |
|||
for i in range(0, 10): |
|||
nclasses = 2 ** i |
|||
with open("test.cpp", "w") as f: |
|||
f.write(codegen(nclasses)) |
|||
n1 = dt.datetime.now() |
|||
os.system("g++ -Os -shared -rdynamic -undefined dynamic_lookup " |
|||
"-fvisibility=hidden -std=c++14 test.cpp -I include " |
|||
"-I /System/Library/Frameworks/Python.framework/Headers -o test.so") |
|||
n2 = dt.datetime.now() |
|||
elapsed = (n2 - n1).total_seconds() |
|||
size = os.stat('test.so').st_size |
|||
print(" {%i, %f, %i}," % (nclasses * nfns, elapsed, size)) |
|||
print ("}") |
|||
@ -1,96 +0,0 @@ |
|||
Benchmark |
|||
========= |
|||
|
|||
The following is the result of a synthetic benchmark comparing both compilation |
|||
time and module size of pybind11 against Boost.Python. |
|||
|
|||
Setup |
|||
----- |
|||
|
|||
A python script (see the ``docs/benchmark.py`` file) was used to generate a set |
|||
of files with dummy classes whose count increases for each successive benchmark |
|||
(between 1 and 2048 classes in powers of two). Each class has four methods with |
|||
a randomly generated signature with a return value and four arguments. (There |
|||
was no particular reason for this setup other than the desire to generate many |
|||
unique function signatures whose count could be controlled in a simple way.) |
|||
|
|||
Here is an example of the binding code for one class: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
... |
|||
class cl034 { |
|||
public: |
|||
cl279 *fn_000(cl084 *, cl057 *, cl065 *, cl042 *); |
|||
cl025 *fn_001(cl098 *, cl262 *, cl414 *, cl121 *); |
|||
cl085 *fn_002(cl445 *, cl297 *, cl145 *, cl421 *); |
|||
cl470 *fn_003(cl200 *, cl323 *, cl332 *, cl492 *); |
|||
}; |
|||
... |
|||
|
|||
PYBIND11_PLUGIN(example) { |
|||
py::module m("example"); |
|||
... |
|||
py::class_<cl034>(m, "cl034") |
|||
.def("fn_000", &cl034::fn_000) |
|||
.def("fn_001", &cl034::fn_001) |
|||
.def("fn_002", &cl034::fn_002) |
|||
.def("fn_003", &cl034::fn_003) |
|||
... |
|||
return m.ptr(); |
|||
} |
|||
|
|||
The Boost.Python version looks almost identical except that a return value |
|||
policy had to be specified as an argument to ``def()``. For both libraries, |
|||
compilation was done with |
|||
|
|||
.. code-block:: bash |
|||
|
|||
Apple LLVM version 7.0.2 (clang-700.1.81) |
|||
|
|||
and the following compilation flags |
|||
|
|||
.. code-block:: bash |
|||
|
|||
g++ -Os -shared -rdynamic -undefined dynamic_lookup -fvisibility=hidden -std=c++14 |
|||
|
|||
Compilation time |
|||
---------------- |
|||
|
|||
The following log-log plot shows how the compilation time grows for an |
|||
increasing number of class and function declarations. pybind11 includes many |
|||
fewer headers, which initially leads to shorter compilation times, but the |
|||
performance is ultimately fairly similar (pybind11 is 19.8 seconds faster for |
|||
the largest largest file with 2048 classes and a total of 8192 methods -- a |
|||
modest **1.2x** speedup relative to Boost.Python, which required 116.35 |
|||
seconds). |
|||
|
|||
.. only:: not latex |
|||
|
|||
.. image:: pybind11_vs_boost_python1.svg |
|||
|
|||
.. only:: latex |
|||
|
|||
.. image:: pybind11_vs_boost_python1.png |
|||
|
|||
Module size |
|||
----------- |
|||
|
|||
Differences between the two libraries become much more pronounced when |
|||
considering the file size of the generated Python plugin: for the largest file, |
|||
the binary generated by Boost.Python required 16.8 MiB, which was **2.17 |
|||
times** / **9.1 megabytes** larger than the output generated by pybind11. For |
|||
very small inputs, Boost.Python has an edge in the plot below -- however, note |
|||
that it stores many definitions in an external library, whose size was not |
|||
included here, hence the comparison is slightly shifted in Boost.Python's |
|||
favor. |
|||
|
|||
.. only:: not latex |
|||
|
|||
.. image:: pybind11_vs_boost_python2.svg |
|||
|
|||
.. only:: latex |
|||
|
|||
.. image:: pybind11_vs_boost_python2.png |
|||
|
|||
|
|||
@ -1,172 +0,0 @@ |
|||
.. _changelog: |
|||
|
|||
Changelog |
|||
######### |
|||
|
|||
Starting with version 1.8, pybind11 releases use a |
|||
[semantic versioning](http://semver.org) policy. |
|||
|
|||
Breaking changes queued for v2.0.0 (Not yet released) |
|||
----------------------------------------------------- |
|||
* Redesigned virtual call mechanism and user-facing syntax (see |
|||
https://github.com/pybind/pybind11/commit/86d825f3302701d81414ddd3d38bcd09433076bc) |
|||
|
|||
* Remove ``handle.call()`` method |
|||
|
|||
1.9.0 (Not yet released) |
|||
------------------------ |
|||
* Queued changes: ``py::eval*``, map indexing suite, documentation for indexing suites. |
|||
|
|||
1.8.0 (June 14, 2016) |
|||
---------------------- |
|||
* Redesigned CMake build system which exports a convenient |
|||
``pybind11_add_module`` function to parent projects. |
|||
* ``std::vector<>`` type bindings analogous to Boost.Python's ``indexing_suite`` |
|||
* Transparent conversion of sparse and dense Eigen matrices and vectors (``eigen.h``) |
|||
* Added an ``ExtraFlags`` template argument to the NumPy ``array_t<>`` wrapper |
|||
to disable an enforced cast that may lose precision, e.g. to create overloads |
|||
for different precisions and complex vs real-valued matrices. |
|||
* Prevent implicit conversion of floating point values to integral types in |
|||
function arguments |
|||
* Fixed incorrect default return value policy for functions returning a shared |
|||
pointer |
|||
* Don't allow registering a type via ``class_`` twice |
|||
* Don't allow casting a ``None`` value into a C++ lvalue reference |
|||
* Fixed a crash in ``enum_::operator==`` that was triggered by the ``help()`` command |
|||
* Improved detection of whether or not custom C++ types can be copy/move-constructed |
|||
* Extended ``str`` type to also work with ``bytes`` instances |
|||
* Added a ``"name"_a`` user defined string literal that is equivalent to ``py::arg("name")``. |
|||
* When specifying function arguments via ``py::arg``, the test that verifies |
|||
the number of arguments now runs at compile time. |
|||
* Added ``[[noreturn]]`` attribute to ``pybind11_fail()`` to quench some |
|||
compiler warnings |
|||
* List function arguments in exception text when the dispatch code cannot find |
|||
a matching overload |
|||
* Added ``PYBIND11_OVERLOAD_NAME`` and ``PYBIND11_OVERLOAD_PURE_NAME`` macros which |
|||
can be used to override virtual methods whose name differs in C++ and Python |
|||
(e.g. ``__call__`` and ``operator()``) |
|||
* Various minor ``iterator`` and ``make_iterator()`` improvements |
|||
* Transparently support ``__bool__`` on Python 2.x and Python 3.x |
|||
* Fixed issue with destructor of unpickled object not being called |
|||
* Minor CMake build system improvements on Windows |
|||
* New ``pybind11::args`` and ``pybind11::kwargs`` types to create functions which |
|||
take an arbitrary number of arguments and keyword arguments |
|||
* New syntax to call a Python function from C++ using ``*args`` and ``*kwargs`` |
|||
* The functions ``def_property_*`` now correctly process docstring arguments (these |
|||
formerly caused a segmentation fault) |
|||
* Many ``mkdoc.py`` improvements (enumerations, template arguments, ``DOC()`` |
|||
macro accepts more arguments) |
|||
* Cygwin support |
|||
* Documentation improvements (pickling support, ``keep_alive``, macro usage) |
|||
|
|||
1.7 (April 30, 2016) |
|||
---------------------- |
|||
* Added a new ``move`` return value policy that triggers C++11 move semantics. |
|||
The automatic return value policy falls back to this case whenever a rvalue |
|||
reference is encountered |
|||
* Significantly more general GIL state routines that are used instead of |
|||
Python's troublesome ``PyGILState_Ensure`` and ``PyGILState_Release`` API |
|||
* Redesign of opaque types that drastically simplifies their usage |
|||
* Extended ability to pass values of type ``[const] void *`` |
|||
* ``keep_alive`` fix: don't fail when there is no patient |
|||
* ``functional.h``: acquire the GIL before calling a Python function |
|||
* Added Python RAII type wrappers ``none`` and ``iterable`` |
|||
* Added ``*args`` and ``*kwargs`` pass-through parameters to |
|||
``pybind11.get_include()`` function |
|||
* Iterator improvements and fixes |
|||
* Documentation on return value policies and opaque types improved |
|||
|
|||
1.6 (April 30, 2016) |
|||
---------------------- |
|||
* Skipped due to upload to PyPI gone wrong and inability to recover |
|||
(https://github.com/pypa/packaging-problems/issues/74) |
|||
|
|||
1.5 (April 21, 2016) |
|||
---------------------- |
|||
* For polymorphic types, use RTTI to try to return the closest type registered with pybind11 |
|||
* Pickling support for serializing and unserializing C++ instances to a byte stream in Python |
|||
* Added a convenience routine ``make_iterator()`` which turns a range indicated |
|||
by a pair of C++ iterators into a iterable Python object |
|||
* Added ``len()`` and a variadic ``make_tuple()`` function |
|||
* Addressed a rare issue that could confuse the current virtual function |
|||
dispatcher and another that could lead to crashes in multi-threaded |
|||
applications |
|||
* Added a ``get_include()`` function to the Python module that returns the path |
|||
of the directory containing the installed pybind11 header files |
|||
* Documentation improvements: import issues, symbol visibility, pickling, limitations |
|||
* Added casting support for ``std::reference_wrapper<>`` |
|||
|
|||
1.4 (April 7, 2016) |
|||
-------------------------- |
|||
* Transparent type conversion for ``std::wstring`` and ``wchar_t`` |
|||
* Allow passing ``nullptr``-valued strings |
|||
* Transparent passing of ``void *`` pointers using capsules |
|||
* Transparent support for returning values wrapped in ``std::unique_ptr<>`` |
|||
* Improved docstring generation for compatibility with Sphinx |
|||
* Nicer debug error message when default parameter construction fails |
|||
* Support for "opaque" types that bypass the transparent conversion layer for STL containers |
|||
* Redesigned type casting interface to avoid ambiguities that could occasionally cause compiler errors |
|||
* Redesigned property implementation; fixes crashes due to an unfortunate default return value policy |
|||
* Anaconda package generation support |
|||
|
|||
1.3 (March 8, 2016) |
|||
-------------------------- |
|||
|
|||
* Added support for the Intel C++ compiler (v15+) |
|||
* Added support for the STL unordered set/map data structures |
|||
* Added support for the STL linked list data structure |
|||
* NumPy-style broadcasting support in ``pybind11::vectorize`` |
|||
* pybind11 now displays more verbose error messages when ``arg::operator=()`` fails |
|||
* pybind11 internal data structures now live in a version-dependent namespace to avoid ABI issues |
|||
* Many, many bugfixes involving corner cases and advanced usage |
|||
|
|||
1.2 (February 7, 2016) |
|||
-------------------------- |
|||
|
|||
* Optional: efficient generation of function signatures at compile time using C++14 |
|||
* Switched to a simpler and more general way of dealing with function default |
|||
arguments. Unused keyword arguments in function calls are now detected and |
|||
cause errors as expected |
|||
* New ``keep_alive`` call policy analogous to Boost.Python's ``with_custodian_and_ward`` |
|||
* New ``pybind11::base<>`` attribute to indicate a subclass relationship |
|||
* Improved interface for RAII type wrappers in ``pytypes.h`` |
|||
* Use RAII type wrappers consistently within pybind11 itself. This |
|||
fixes various potential refcount leaks when exceptions occur |
|||
* Added new ``bytes`` RAII type wrapper (maps to ``string`` in Python 2.7) |
|||
* Made handle and related RAII classes const correct, using them more |
|||
consistently everywhere now |
|||
* Got rid of the ugly ``__pybind11__`` attributes on the Python side---they are |
|||
now stored in a C++ hash table that is not visible in Python |
|||
* Fixed refcount leaks involving NumPy arrays and bound functions |
|||
* Vastly improved handling of shared/smart pointers |
|||
* Removed an unnecessary copy operation in ``pybind11::vectorize`` |
|||
* Fixed naming clashes when both pybind11 and NumPy headers are included |
|||
* Added conversions for additional exception types |
|||
* Documentation improvements (using multiple extension modules, smart pointers, |
|||
other minor clarifications) |
|||
* unified infrastructure for parsing variadic arguments in ``class_`` and cpp_function |
|||
* Fixed license text (was: ZLIB, should have been: 3-clause BSD) |
|||
* Python 3.2 compatibility |
|||
* Fixed remaining issues when accessing types in another plugin module |
|||
* Added enum comparison and casting methods |
|||
* Improved SFINAE-based detection of whether types are copy-constructible |
|||
* Eliminated many warnings about unused variables and the use of ``offsetof()`` |
|||
* Support for ``std::array<>`` conversions |
|||
|
|||
1.1 (December 7, 2015) |
|||
-------------------------- |
|||
|
|||
* Documentation improvements (GIL, wrapping functions, casting, fixed many typos) |
|||
* Generalized conversion of integer types |
|||
* Improved support for casting function objects |
|||
* Improved support for ``std::shared_ptr<>`` conversions |
|||
* Initial support for ``std::set<>`` conversions |
|||
* Fixed type resolution issue for types defined in a separate plugin module |
|||
* Cmake build system improvements |
|||
* Factored out generic functionality to non-templated code (smaller code size) |
|||
* Added a code size / compile time benchmark vs Boost.Python |
|||
* Added an appveyor CI script |
|||
|
|||
1.0 (October 15, 2015) |
|||
------------------------ |
|||
* Initial release |
|||
@ -1,334 +0,0 @@ |
|||
.. _classes: |
|||
|
|||
Object-oriented code |
|||
#################### |
|||
|
|||
Creating bindings for a custom type |
|||
=================================== |
|||
|
|||
Let's now look at a more complex example where we'll create bindings for a |
|||
custom C++ data structure named ``Pet``. Its definition is given below: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
struct Pet { |
|||
Pet(const std::string &name) : name(name) { } |
|||
void setName(const std::string &name_) { name = name_; } |
|||
const std::string &getName() const { return name; } |
|||
|
|||
std::string name; |
|||
}; |
|||
|
|||
The binding code for ``Pet`` looks as follows: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
#include <pybind11/pybind11.h> |
|||
|
|||
namespace py = pybind11; |
|||
|
|||
PYBIND11_PLUGIN(example) { |
|||
py::module m("example", "pybind11 example plugin"); |
|||
|
|||
py::class_<Pet>(m, "Pet") |
|||
.def(py::init<const std::string &>()) |
|||
.def("setName", &Pet::setName) |
|||
.def("getName", &Pet::getName); |
|||
|
|||
return m.ptr(); |
|||
} |
|||
|
|||
:class:`class_` creates bindings for a C++ `class` or `struct`-style data |
|||
structure. :func:`init` is a convenience function that takes the types of a |
|||
constructor's parameters as template arguments and wraps the corresponding |
|||
constructor (see the :ref:`custom_constructors` section for details). An |
|||
interactive Python session demonstrating this example is shown below: |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
% python |
|||
>>> import example |
|||
>>> p = example.Pet('Molly') |
|||
>>> print(p) |
|||
<example.Pet object at 0x10cd98060> |
|||
>>> p.getName() |
|||
u'Molly' |
|||
>>> p.setName('Charly') |
|||
>>> p.getName() |
|||
u'Charly' |
|||
|
|||
.. seealso:: |
|||
|
|||
Static member functions can be bound in the same way using |
|||
:func:`class_::def_static`. |
|||
|
|||
Keyword and default arguments |
|||
============================= |
|||
It is possible to specify keyword and default arguments using the syntax |
|||
discussed in the previous chapter. Refer to the sections :ref:`keyword_args` |
|||
and :ref:`default_args` for details. |
|||
|
|||
Binding lambda functions |
|||
======================== |
|||
|
|||
Note how ``print(p)`` produced a rather useless summary of our data structure in the example above: |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> print(p) |
|||
<example.Pet object at 0x10cd98060> |
|||
|
|||
To address this, we could bind an utility function that returns a human-readable |
|||
summary to the special method slot named ``__repr__``. Unfortunately, there is no |
|||
suitable functionality in the ``Pet`` data structure, and it would be nice if |
|||
we did not have to change it. This can easily be accomplished by binding a |
|||
Lambda function instead: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
py::class_<Pet>(m, "Pet") |
|||
.def(py::init<const std::string &>()) |
|||
.def("setName", &Pet::setName) |
|||
.def("getName", &Pet::getName) |
|||
.def("__repr__", |
|||
[](const Pet &a) { |
|||
return "<example.Pet named '" + a.name + "'>"; |
|||
} |
|||
); |
|||
|
|||
Both stateless [#f1]_ and stateful lambda closures are supported by pybind11. |
|||
With the above change, the same Python code now produces the following output: |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> print(p) |
|||
<example.Pet named 'Molly'> |
|||
|
|||
.. _properties: |
|||
|
|||
Instance and static fields |
|||
========================== |
|||
|
|||
We can also directly expose the ``name`` field using the |
|||
:func:`class_::def_readwrite` method. A similar :func:`class_::def_readonly` |
|||
method also exists for ``const`` fields. |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
py::class_<Pet>(m, "Pet") |
|||
.def(py::init<const std::string &>()) |
|||
.def_readwrite("name", &Pet::name) |
|||
// ... remainder ... |
|||
|
|||
This makes it possible to write |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> p = example.Pet('Molly') |
|||
>>> p.name |
|||
u'Molly' |
|||
>>> p.name = 'Charly' |
|||
>>> p.name |
|||
u'Charly' |
|||
|
|||
Now suppose that ``Pet::name`` was a private internal variable |
|||
that can only be accessed via setters and getters. |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
class Pet { |
|||
public: |
|||
Pet(const std::string &name) : name(name) { } |
|||
void setName(const std::string &name_) { name = name_; } |
|||
const std::string &getName() const { return name; } |
|||
private: |
|||
std::string name; |
|||
}; |
|||
|
|||
In this case, the method :func:`class_::def_property` |
|||
(:func:`class_::def_property_readonly` for read-only data) can be used to |
|||
provide a field-like interface within Python that will transparently call |
|||
the setter and getter functions: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
py::class_<Pet>(m, "Pet") |
|||
.def(py::init<const std::string &>()) |
|||
.def_property("name", &Pet::getName, &Pet::setName) |
|||
// ... remainder ... |
|||
|
|||
.. seealso:: |
|||
|
|||
Similar functions :func:`class_::def_readwrite_static`, |
|||
:func:`class_::def_readonly_static` :func:`class_::def_property_static`, |
|||
and :func:`class_::def_property_readonly_static` are provided for binding |
|||
static variables and properties. Please also see the section on |
|||
:ref:`static_properties` in the advanced part of the documentation. |
|||
|
|||
.. _inheritance: |
|||
|
|||
Inheritance |
|||
=========== |
|||
|
|||
Suppose now that the example consists of two data structures with an |
|||
inheritance relationship: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
struct Pet { |
|||
Pet(const std::string &name) : name(name) { } |
|||
std::string name; |
|||
}; |
|||
|
|||
struct Dog : Pet { |
|||
Dog(const std::string &name) : Pet(name) { } |
|||
std::string bark() const { return "woof!"; } |
|||
}; |
|||
|
|||
There are two different ways of indicating a hierarchical relationship to |
|||
pybind11: the first is by specifying the C++ base class explicitly during |
|||
construction using the ``base`` attribute: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
py::class_<Pet>(m, "Pet") |
|||
.def(py::init<const std::string &>()) |
|||
.def_readwrite("name", &Pet::name); |
|||
|
|||
py::class_<Dog>(m, "Dog", py::base<Pet>() /* <- specify C++ parent type */) |
|||
.def(py::init<const std::string &>()) |
|||
.def("bark", &Dog::bark); |
|||
|
|||
Alternatively, we can also assign a name to the previously bound ``Pet`` |
|||
:class:`class_` object and reference it when binding the ``Dog`` class: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
py::class_<Pet> pet(m, "Pet"); |
|||
pet.def(py::init<const std::string &>()) |
|||
.def_readwrite("name", &Pet::name); |
|||
|
|||
py::class_<Dog>(m, "Dog", pet /* <- specify Python parent type */) |
|||
.def(py::init<const std::string &>()) |
|||
.def("bark", &Dog::bark); |
|||
|
|||
Functionality-wise, both approaches are completely equivalent. Afterwards, |
|||
instances will expose fields and methods of both types: |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> p = example.Dog('Molly') |
|||
>>> p.name |
|||
u'Molly' |
|||
>>> p.bark() |
|||
u'woof!' |
|||
|
|||
Overloaded methods |
|||
================== |
|||
|
|||
Sometimes there are several overloaded C++ methods with the same name taking |
|||
different kinds of input arguments: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
struct Pet { |
|||
Pet(const std::string &name, int age) : name(name), age(age) { } |
|||
|
|||
void set(int age) { age = age; } |
|||
void set(const std::string &name) { name = name; } |
|||
|
|||
std::string name; |
|||
int age; |
|||
}; |
|||
|
|||
Attempting to bind ``Pet::set`` will cause an error since the compiler does not |
|||
know which method the user intended to select. We can disambiguate by casting |
|||
them to function pointers. Binding multiple functions to the same Python name |
|||
automatically creates a chain of function overloads that will be tried in |
|||
sequence. |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
py::class_<Pet>(m, "Pet") |
|||
.def(py::init<const std::string &, int>()) |
|||
.def("set", (void (Pet::*)(int)) &Pet::set, "Set the pet's age") |
|||
.def("set", (void (Pet::*)(const std::string &)) &Pet::set, "Set the pet's name"); |
|||
|
|||
The overload signatures are also visible in the method's docstring: |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> help(example.Pet) |
|||
|
|||
class Pet(__builtin__.object) |
|||
| Methods defined here: |
|||
| |
|||
| __init__(...) |
|||
| Signature : (Pet, str, int) -> NoneType |
|||
| |
|||
| set(...) |
|||
| 1. Signature : (Pet, int) -> NoneType |
|||
| |
|||
| Set the pet's age |
|||
| |
|||
| 2. Signature : (Pet, str) -> NoneType |
|||
| |
|||
| Set the pet's name |
|||
|
|||
.. note:: |
|||
|
|||
To define multiple overloaded constructors, simply declare one after the |
|||
other using the ``.def(py::init<...>())`` syntax. The existing machinery |
|||
for specifying keyword and default arguments also works. |
|||
|
|||
Enumerations and internal types |
|||
=============================== |
|||
|
|||
Let's now suppose that the example class contains an internal enumeration type, |
|||
e.g.: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
struct Pet { |
|||
enum Kind { |
|||
Dog = 0, |
|||
Cat |
|||
}; |
|||
|
|||
Pet(const std::string &name, Kind type) : name(name), type(type) { } |
|||
|
|||
std::string name; |
|||
Kind type; |
|||
}; |
|||
|
|||
The binding code for this example looks as follows: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
py::class_<Pet> pet(m, "Pet"); |
|||
|
|||
pet.def(py::init<const std::string &, Pet::Kind>()) |
|||
.def_readwrite("name", &Pet::name) |
|||
.def_readwrite("type", &Pet::type); |
|||
|
|||
py::enum_<Pet::Kind>(pet, "Kind") |
|||
.value("Dog", Pet::Kind::Dog) |
|||
.value("Cat", Pet::Kind::Cat) |
|||
.export_values(); |
|||
|
|||
To ensure that the ``Kind`` type is created within the scope of ``Pet``, the |
|||
``pet`` :class:`class_` instance must be supplied to the :class:`enum_`. |
|||
constructor. The :func:`enum_::export_values` function exports the enum entries |
|||
into the parent scope, which should be skipped for newer C++11-style strongly |
|||
typed enums. |
|||
|
|||
.. code-block:: pycon |
|||
|
|||
>>> p = Pet('Lucy', Pet.Cat) |
|||
>>> p.type |
|||
Kind.Cat |
|||
>>> int(p.type) |
|||
1L |
|||
|
|||
|
|||
.. [#f1] Stateless closures are those with an empty pair of brackets ``[]`` as the capture object. |
|||
@ -1,53 +0,0 @@ |
|||
Build systems |
|||
############# |
|||
|
|||
Building with setuptools |
|||
======================== |
|||
|
|||
For projects on PyPI, building with setuptools is the way to go. Sylvain Corlay |
|||
has kindly provided an example project which shows how to set up everything, |
|||
including automatic generation of documentation using Sphinx. Please refer to |
|||
the [python_example]_ repository. |
|||
|
|||
.. [python_example] https://github.com/pybind/python_example |
|||
|
|||
Building with cppimport |
|||
======================== |
|||
|
|||
cppimport is a small Python import hook that determines whether there is a C++ |
|||
source file whose name matches the requested module. If there is, the file is |
|||
compiled as a Python extension using pybind11 and placed in the same folder as |
|||
the C++ source file. Python is then able to find the module and load it. |
|||
|
|||
.. [cppimport] https://github.com/tbenthompson/cppimport |
|||
|
|||
.. _cmake: |
|||
|
|||
Building with CMake |
|||
=================== |
|||
|
|||
For C++ codebases that have an existing CMake-based build system, a Python |
|||
extension module can be created with just a few lines of code: |
|||
|
|||
.. code-block:: cmake |
|||
|
|||
cmake_minimum_required(VERSION 2.8.12) |
|||
project(example) |
|||
|
|||
add_subdirectory(pybind11) |
|||
pybind11_add_module(example example.cpp) |
|||
|
|||
This assumes that the pybind11 repository is located in a subdirectory named |
|||
:file:`pybind11` and that the code is located in a file named :file:`example.cpp`. |
|||
The CMake command ``add_subdirectory`` will import a function with the signature |
|||
``pybind11_add_module(<name> source1 [source2 ...])``. It will take care of all |
|||
the details needed to build a Python extension module on any platform. |
|||
|
|||
The target Python version can be selected by setting the ``PYBIND11_PYTHON_VERSION`` |
|||
variable before adding the pybind11 subdirectory. Alternatively, an exact Python |
|||
installation can be specified by setting ``PYTHON_EXECUTABLE``. |
|||
|
|||
A working sample project, including a way to invoke CMake from :file:`setup.py` for |
|||
PyPI integration, can be found in the [cmake_example]_ repository. |
|||
|
|||
.. [cmake_example] https://github.com/pybind/cmake_example |
|||
@ -1,308 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# pybind11 documentation build configuration file, created by |
|||
# sphinx-quickstart on Sun Oct 11 19:23:48 2015. |
|||
# |
|||
# This file is execfile()d with the current directory set to its |
|||
# containing dir. |
|||
# |
|||
# Note that not all possible configuration values are present in this |
|||
# autogenerated file. |
|||
# |
|||
# All configuration values have a default; values that are commented out |
|||
# serve to show the default. |
|||
|
|||
import sys |
|||
import os |
|||
import shlex |
|||
|
|||
# If extensions (or modules to document with autodoc) are in another directory, |
|||
# add these directories to sys.path here. If the directory is relative to the |
|||
# documentation root, use os.path.abspath to make it absolute, like shown here. |
|||
#sys.path.insert(0, os.path.abspath('.')) |
|||
|
|||
# -- General configuration ------------------------------------------------ |
|||
|
|||
# If your documentation needs a minimal Sphinx version, state it here. |
|||
#needs_sphinx = '1.0' |
|||
|
|||
# Add any Sphinx extension module names here, as strings. They can be |
|||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom |
|||
# ones. |
|||
extensions = [] |
|||
|
|||
# Add any paths that contain templates here, relative to this directory. |
|||
templates_path = ['.templates'] |
|||
|
|||
# The suffix(es) of source filenames. |
|||
# You can specify multiple suffix as a list of string: |
|||
# source_suffix = ['.rst', '.md'] |
|||
source_suffix = '.rst' |
|||
|
|||
# The encoding of source files. |
|||
#source_encoding = 'utf-8-sig' |
|||
|
|||
# The master toctree document. |
|||
master_doc = 'index' |
|||
|
|||
# General information about the project. |
|||
project = 'pybind11' |
|||
copyright = '2015, Wenzel Jakob' |
|||
author = 'Wenzel Jakob' |
|||
|
|||
# The version info for the project you're documenting, acts as replacement for |
|||
# |version| and |release|, also used in various other places throughout the |
|||
# built documents. |
|||
# |
|||
# The short X.Y version. |
|||
version = '1.9' |
|||
# The full version, including alpha/beta/rc tags. |
|||
release = '1.9.dev0' |
|||
|
|||
# The language for content autogenerated by Sphinx. Refer to documentation |
|||
# for a list of supported languages. |
|||
# |
|||
# This is also used if you do content translation via gettext catalogs. |
|||
# Usually you set "language" from the command line for these cases. |
|||
language = None |
|||
|
|||
# There are two options for replacing |today|: either, you set today to some |
|||
# non-false value, then it is used: |
|||
#today = '' |
|||
# Else, today_fmt is used as the format for a strftime call. |
|||
#today_fmt = '%B %d, %Y' |
|||
|
|||
# List of patterns, relative to source directory, that match files and |
|||
# directories to ignore when looking for source files. |
|||
exclude_patterns = ['.build'] |
|||
|
|||
# The reST default role (used for this markup: `text`) to use for all |
|||
# documents. |
|||
#default_role = None |
|||
|
|||
# If true, '()' will be appended to :func: etc. cross-reference text. |
|||
#add_function_parentheses = True |
|||
|
|||
# If true, the current module name will be prepended to all description |
|||
# unit titles (such as .. function::). |
|||
#add_module_names = True |
|||
|
|||
# If true, sectionauthor and moduleauthor directives will be shown in the |
|||
# output. They are ignored by default. |
|||
#show_authors = False |
|||
|
|||
# The name of the Pygments (syntax highlighting) style to use. |
|||
#pygments_style = 'monokai' |
|||
|
|||
# A list of ignored prefixes for module index sorting. |
|||
#modindex_common_prefix = [] |
|||
|
|||
# If true, keep warnings as "system message" paragraphs in the built documents. |
|||
#keep_warnings = False |
|||
|
|||
# If true, `todo` and `todoList` produce output, else they produce nothing. |
|||
todo_include_todos = False |
|||
|
|||
|
|||
# -- Options for HTML output ---------------------------------------------- |
|||
|
|||
# The theme to use for HTML and HTML Help pages. See the documentation for |
|||
# a list of builtin themes. |
|||
|
|||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True' |
|||
|
|||
if not on_rtd: # only import and set the theme if we're building docs locally |
|||
import sphinx_rtd_theme |
|||
html_theme = 'sphinx_rtd_theme' |
|||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] |
|||
|
|||
html_context = { |
|||
'css_files': [ |
|||
'_static/theme_overrides.css' |
|||
] |
|||
} |
|||
else: |
|||
html_context = { |
|||
'css_files': [ |
|||
'//media.readthedocs.org/css/sphinx_rtd_theme.css', |
|||
'//media.readthedocs.org/css/readthedocs-doc-embed.css', |
|||
'_static/theme_overrides.css' |
|||
] |
|||
} |
|||
|
|||
# Theme options are theme-specific and customize the look and feel of a theme |
|||
# further. For a list of options available for each theme, see the |
|||
# documentation. |
|||
#html_theme_options = {} |
|||
|
|||
# Add any paths that contain custom themes here, relative to this directory. |
|||
#html_theme_path = [] |
|||
|
|||
# The name for this set of Sphinx documents. If None, it defaults to |
|||
# "<project> v<release> documentation". |
|||
#html_title = None |
|||
|
|||
# A shorter title for the navigation bar. Default is the same as html_title. |
|||
#html_short_title = None |
|||
|
|||
# The name of an image file (relative to this directory) to place at the top |
|||
# of the sidebar. |
|||
#html_logo = None |
|||
|
|||
# The name of an image file (within the static path) to use as favicon of the |
|||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 |
|||
# pixels large. |
|||
#html_favicon = None |
|||
|
|||
# Add any paths that contain custom static files (such as style sheets) here, |
|||
# relative to this directory. They are copied after the builtin static files, |
|||
# so a file named "default.css" will overwrite the builtin "default.css". |
|||
html_static_path = ['_static'] |
|||
|
|||
# Add any extra paths that contain custom files (such as robots.txt or |
|||
# .htaccess) here, relative to this directory. These files are copied |
|||
# directly to the root of the documentation. |
|||
#html_extra_path = [] |
|||
|
|||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, |
|||
# using the given strftime format. |
|||
#html_last_updated_fmt = '%b %d, %Y' |
|||
|
|||
# If true, SmartyPants will be used to convert quotes and dashes to |
|||
# typographically correct entities. |
|||
#html_use_smartypants = True |
|||
|
|||
# Custom sidebar templates, maps document names to template names. |
|||
#html_sidebars = {} |
|||
|
|||
# Additional templates that should be rendered to pages, maps page names to |
|||
# template names. |
|||
#html_additional_pages = {} |
|||
|
|||
# If false, no module index is generated. |
|||
#html_domain_indices = True |
|||
|
|||
# If false, no index is generated. |
|||
#html_use_index = True |
|||
|
|||
# If true, the index is split into individual pages for each letter. |
|||
#html_split_index = False |
|||
|
|||
# If true, links to the reST sources are added to the pages. |
|||
#html_show_sourcelink = True |
|||
|
|||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. |
|||
#html_show_sphinx = True |
|||
|
|||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. |
|||
#html_show_copyright = True |
|||
|
|||
# If true, an OpenSearch description file will be output, and all pages will |
|||
# contain a <link> tag referring to it. The value of this option must be the |
|||
# base URL from which the finished HTML is served. |
|||
#html_use_opensearch = '' |
|||
|
|||
# This is the file name suffix for HTML files (e.g. ".xhtml"). |
|||
#html_file_suffix = None |
|||
|
|||
# Language to be used for generating the HTML full-text search index. |
|||
# Sphinx supports the following languages: |
|||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' |
|||
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' |
|||
#html_search_language = 'en' |
|||
|
|||
# A dictionary with options for the search language support, empty by default. |
|||
# Now only 'ja' uses this config value |
|||
#html_search_options = {'type': 'default'} |
|||
|
|||
# The name of a javascript file (relative to the configuration directory) that |
|||
# implements a search results scorer. If empty, the default will be used. |
|||
#html_search_scorer = 'scorer.js' |
|||
|
|||
# Output file base name for HTML help builder. |
|||
htmlhelp_basename = 'pybind11doc' |
|||
|
|||
# -- Options for LaTeX output --------------------------------------------- |
|||
|
|||
latex_elements = { |
|||
# The paper size ('letterpaper' or 'a4paper'). |
|||
#'papersize': 'letterpaper', |
|||
|
|||
# The font size ('10pt', '11pt' or '12pt'). |
|||
#'pointsize': '10pt', |
|||
|
|||
# Additional stuff for the LaTeX preamble. |
|||
'preamble': '\DeclareUnicodeCharacter{00A0}{}', |
|||
|
|||
# Latex figure (float) alignment |
|||
#'figure_align': 'htbp', |
|||
} |
|||
|
|||
# Grouping the document tree into LaTeX files. List of tuples |
|||
# (source start file, target name, title, |
|||
# author, documentclass [howto, manual, or own class]). |
|||
latex_documents = [ |
|||
(master_doc, 'pybind11.tex', 'pybind11 Documentation', |
|||
'Wenzel Jakob', 'manual'), |
|||
] |
|||
|
|||
# The name of an image file (relative to this directory) to place at the top of |
|||
# the title page. |
|||
# latex_logo = 'pybind11-logo.png' |
|||
|
|||
# For "manual" documents, if this is true, then toplevel headings are parts, |
|||
# not chapters. |
|||
#latex_use_parts = False |
|||
|
|||
# If true, show page references after internal links. |
|||
#latex_show_pagerefs = False |
|||
|
|||
# If true, show URL addresses after external links. |
|||
#latex_show_urls = False |
|||
|
|||
# Documents to append as an appendix to all manuals. |
|||
#latex_appendices = [] |
|||
|
|||
# If false, no module index is generated. |
|||
#latex_domain_indices = True |
|||
|
|||
|
|||
# -- Options for manual page output --------------------------------------- |
|||
|
|||
# One entry per manual page. List of tuples |
|||
# (source start file, name, description, authors, manual section). |
|||
man_pages = [ |
|||
(master_doc, 'pybind11', 'pybind11 Documentation', |
|||
[author], 1) |
|||
] |
|||
|
|||
# If true, show URL addresses after external links. |
|||
#man_show_urls = False |
|||
|
|||
|
|||
# -- Options for Texinfo output ------------------------------------------- |
|||
|
|||
# Grouping the document tree into Texinfo files. List of tuples |
|||
# (source start file, target name, title, author, |
|||
# dir menu entry, description, category) |
|||
texinfo_documents = [ |
|||
(master_doc, 'pybind11', 'pybind11 Documentation', |
|||
author, 'pybind11', 'One line description of project.', |
|||
'Miscellaneous'), |
|||
] |
|||
|
|||
# Documents to append as an appendix to all manuals. |
|||
#texinfo_appendices = [] |
|||
|
|||
# If false, no module index is generated. |
|||
#texinfo_domain_indices = True |
|||
|
|||
# How to display URL addresses: 'footnote', 'no', or 'inline'. |
|||
#texinfo_show_urls = 'footnote' |
|||
|
|||
# If true, do not generate a @detailmenu in the "Top" node's menu. |
|||
#texinfo_no_detailmenu = False |
|||
|
|||
primary_domain = 'cpp' |
|||
highlight_language = 'cpp' |
|||
@ -1,208 +0,0 @@ |
|||
Frequently asked questions |
|||
########################## |
|||
|
|||
"ImportError: dynamic module does not define init function" |
|||
=========================================================== |
|||
|
|||
1. Make sure that the name specified in ``pybind::module`` and |
|||
``PYBIND11_PLUGIN`` is consistent and identical to the filename of the |
|||
extension library. The latter should not contain any extra prefixes (e.g. |
|||
``test.so`` instead of ``libtest.so``). |
|||
|
|||
2. If the above did not fix your issue, then you are likely using an |
|||
incompatible version of Python (for instance, the extension library was |
|||
compiled against Python 2, while the interpreter is running on top of some |
|||
version of Python 3, or vice versa) |
|||
|
|||
"Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" |
|||
======================================================================== |
|||
|
|||
See item 2 of the first answer. |
|||
|
|||
The Python interpreter immediately crashes when importing my module |
|||
=================================================================== |
|||
|
|||
See item 2 of the first answer. |
|||
|
|||
CMake doesn't detect the right Python version |
|||
============================================= |
|||
|
|||
The CMake-based build system will try to automatically detect the installed |
|||
version of Python and link against that. When this fails, or when there are |
|||
multiple versions of Python and it finds the wrong one, delete |
|||
``CMakeCache.txt`` and then invoke CMake as follows: |
|||
|
|||
.. code-block:: bash |
|||
|
|||
cmake -DPYTHON_EXECUTABLE:FILEPATH=<path-to-python-executable> . |
|||
|
|||
Limitations involving reference arguments |
|||
========================================= |
|||
|
|||
In C++, it's fairly common to pass arguments using mutable references or |
|||
mutable pointers, which allows both read and write access to the value |
|||
supplied by the caller. This is sometimes done for efficiency reasons, or to |
|||
realize functions that have multiple return values. Here are two very basic |
|||
examples: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
void increment(int &i) { i++; } |
|||
void increment_ptr(int *i) { (*i)++; } |
|||
|
|||
In Python, all arguments are passed by reference, so there is no general |
|||
issue in binding such code from Python. |
|||
|
|||
However, certain basic Python types (like ``str``, ``int``, ``bool``, |
|||
``float``, etc.) are **immutable**. This means that the following attempt |
|||
to port the function to Python doesn't have the same effect on the value |
|||
provided by the caller -- in fact, it does nothing at all. |
|||
|
|||
.. code-block:: python |
|||
|
|||
def increment(i): |
|||
i += 1 # nope.. |
|||
|
|||
pybind11 is also affected by such language-level conventions, which means that |
|||
binding ``increment`` or ``increment_ptr`` will also create Python functions |
|||
that don't modify their arguments. |
|||
|
|||
Although inconvenient, one workaround is to encapsulate the immutable types in |
|||
a custom type that does allow modifications. |
|||
|
|||
An other alternative involves binding a small wrapper lambda function that |
|||
returns a tuple with all output arguments (see the remainder of the |
|||
documentation for examples on binding lambda functions). An example: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
int foo(int &i) { i++; return 123; } |
|||
|
|||
and the binding code |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
m.def("foo", [](int i) { int rv = foo(i); return std::make_tuple(rv, i); }); |
|||
|
|||
|
|||
How can I reduce the build time? |
|||
================================ |
|||
|
|||
It's good practice to split binding code over multiple files, as is done in |
|||
the included file :file:`example/example.cpp`. |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
void init_ex1(py::module &); |
|||
void init_ex2(py::module &); |
|||
/* ... */ |
|||
|
|||
PYBIND11_PLUGIN(example) { |
|||
py::module m("example", "pybind example plugin"); |
|||
|
|||
init_ex1(m); |
|||
init_ex2(m); |
|||
|
|||
/* ... */ |
|||
|
|||
return m.ptr(); |
|||
} |
|||
|
|||
The various ``init_ex`` functions should be contained in separate files that |
|||
can be compiled independently from another. Following this approach will |
|||
|
|||
1. reduce memory requirements per compilation unit. |
|||
|
|||
2. enable parallel builds (if desired). |
|||
|
|||
3. allow for faster incremental builds. For instance, when a single class |
|||
definiton is changed, only a subset of the binding code will generally need |
|||
to be recompiled. |
|||
|
|||
How can I create smaller binaries? |
|||
================================== |
|||
|
|||
To do its job, pybind11 extensively relies on a programming technique known as |
|||
*template metaprogramming*, which is a way of performing computation at compile |
|||
time using type information. Template metaprogamming usually instantiates code |
|||
involving significant numbers of deeply nested types that are either completely |
|||
removed or reduced to just a few instrutions during the compiler's optimization |
|||
phase. However, due to the nested nature of these types, the resulting symbol |
|||
names in the compiled extension library can be extremely long. For instance, |
|||
the included test suite contains the following symbol: |
|||
|
|||
.. only:: html |
|||
|
|||
.. code-block:: none |
|||
|
|||
__ZN8pybind1112cpp_functionC1Iv8Example2JRNSt3__16vectorINS3_12basic_stringIwNS3_11char_traitsIwEENS3_9allocatorIwEEEENS8_ISA_EEEEEJNS_4nameENS_7siblingENS_9is_methodEA28_cEEEMT0_FT_DpT1_EDpRKT2_ |
|||
|
|||
.. only:: not html |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
__ZN8pybind1112cpp_functionC1Iv8Example2JRNSt3__16vectorINS3_12basic_stringIwNS3_11char_traitsIwEENS3_9allocatorIwEEEENS8_ISA_EEEEEJNS_4nameENS_7siblingENS_9is_methodEA28_cEEEMT0_FT_DpT1_EDpRKT2_ |
|||
|
|||
which is the mangled form of the following function type: |
|||
|
|||
.. code-block:: cpp |
|||
|
|||
pybind11::cpp_function::cpp_function<void, Example2, std::__1::vector<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >, std::__1::allocator<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > > >&, pybind11::name, pybind11::sibling, pybind11::is_method, char [28]>(void (Example2::*)(std::__1::vector<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >, std::__1::allocator<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > > >&), pybind11::name const&, pybind11::sibling const&, pybind11::is_method const&, char const (&) [28]) |
|||
|
|||
The memory needed to store just the mangled name of this function (196 bytes) |
|||
is larger than the actual piece of code (111 bytes) it represents! On the other |
|||
hand, it's silly to even give this function a name -- after all, it's just a |
|||
tiny cog in a bigger piece of machinery that is not exposed to the outside |
|||
world. So we'll generally only want to export symbols for those functions which |
|||
are actually called from the outside. |
|||
|
|||
This can be achieved by specifying the parameter ``-fvisibility=hidden`` to GCC |
|||
and Clang, which sets the default symbol visibility to *hidden*. It's best to |
|||
do this only for release builds, since the symbol names can be helpful in |
|||
debugging sessions. On Visual Studio, symbols are already hidden by default, so |
|||
nothing needs to be done there. Needless to say, this has a tremendous impact |
|||
on the final binary size of the resulting extension library. |
|||
|
|||
Another aspect that can require a fair bit of code are function signature |
|||
descriptions. pybind11 automatically generates human-readable function |
|||
signatures for docstrings, e.g.: |
|||
|
|||
.. code-block:: none |
|||
|
|||
| __init__(...) |
|||
| __init__(*args, **kwargs) |
|||
| Overloaded function. |
|||
| |
|||
| 1. __init__(example.Example1) -> NoneType |
|||
| |
|||
| Docstring for overload #1 goes here |
|||
| |
|||
| 2. __init__(example.Example1, int) -> NoneType |
|||
| |
|||
| Docstring for overload #2 goes here |
|||
| |
|||
| 3. __init__(example.Example1, example.Example1) -> NoneType |
|||
| |
|||
| Docstring for overload #3 goes here |
|||
|
|||
|
|||
In C++11 mode, these are generated at run time using string concatenation, |
|||
which can amount to 10-20% of the size of the resulting binary. If you can, |
|||
enable C++14 language features (using ``-std=c++14`` for GCC/Clang), in which |
|||
case signatures are efficiently pre-generated at compile time. Unfortunately, |
|||
Visual Studio's C++14 support (``constexpr``) is not good enough as of April |
|||
2016, so it always uses the more expensive run-time approach. |
|||
|
|||
Working with ancient Visual Studio 2009 builds on Windows |
|||
========================================================= |
|||
|
|||
The official Windows distributions of Python are compiled using truly |
|||
ancient versions of Visual Studio that lack good C++11 support. Some users |
|||
implicitly assume that it would be impossible to load a plugin built with |
|||
Visual Studio 2015 into a Python distribution that was compiled using Visual |
|||
Studio 2009. However, no such issue exists: it's perfectly legitimate to |
|||
interface DLLs that are built with different compilers and/or C libraries. |
|||
Common gotchas to watch out for involve not ``free()``-ing memory region |
|||
that that were ``malloc()``-ed in another shared library, using data |
|||
structures with incompatible ABIs, and so on. pybind11 is very careful not |
|||
to make these types of mistakes. |
|||
@ -1,24 +0,0 @@ |
|||
.. only: not latex |
|||
|
|||
.. image:: pybind11-logo.png |
|||
|
|||
pybind11 --- Seamless operability between C++11 and Python |
|||
========================================================== |
|||
|
|||
.. only: not latex |
|||
|
|||
Contents: |
|||
|
|||
.. toctree:: |
|||
:maxdepth: 2 |
|||
|
|||
intro |
|||
basics |
|||
classes |
|||
advanced |
|||
compiling |
|||
benchmark |
|||
limitations |
|||
faq |
|||
reference |
|||
changelog |
|||
@ -1,87 +0,0 @@ |
|||
.. image:: pybind11-logo.png |
|||
|
|||
About this project |
|||
================== |
|||
**pybind11** is a lightweight header-only library that exposes C++ types in Python |
|||
and vice versa, mainly to create Python bindings of existing C++ code. Its |
|||
goals and syntax are similar to the excellent `Boost.Python`_ library by David |
|||
Abrahams: to minimize boilerplate code in traditional extension modules by |
|||
inferring type information using compile-time introspection. |
|||
|
|||
.. _Boost.Python: http://www.boost.org/doc/libs/release/libs/python/doc/index.html |
|||
|
|||
The main issue with Boost.Python—and the reason for creating such a similar |
|||
project—is Boost. Boost is an enormously large and complex suite of utility |
|||
libraries that works with almost every C++ compiler in existence. This |
|||
compatibility has its cost: arcane template tricks and workarounds are |
|||
necessary to support the oldest and buggiest of compiler specimens. Now that |
|||
C++11-compatible compilers are widely available, this heavy machinery has |
|||
become an excessively large and unnecessary dependency. |
|||
|
|||
Think of this library as a tiny self-contained version of Boost.Python with |
|||
everything stripped away that isn't relevant for binding generation. Without |
|||
comments, the core header files only require ~2.5K lines of code and depend on |
|||
Python (2.7 or 3.x) and the C++ standard library. This compact implementation |
|||
was possible thanks to some of the new C++11 language features (specifically: |
|||
tuples, lambda functions and variadic templates). Since its creation, this |
|||
library has grown beyond Boost.Python in many ways, leading to dramatically |
|||
simpler binding code in many common situations. |
|||
|
|||
Core features |
|||
************* |
|||
The following core C++ features can be mapped to Python |
|||
|
|||
- Functions accepting and returning custom data structures per value, reference, or pointer |
|||
- Instance methods and static methods |
|||
- Overloaded functions |
|||
- Instance attributes and static attributes |
|||
- Exceptions |
|||
- Enumerations |
|||
- Iterators and ranges |
|||
- Callbacks |
|||
- Custom operators |
|||
- STL data structures |
|||
- Smart pointers with reference counting like ``std::shared_ptr`` |
|||
- Internal references with correct reference counting |
|||
- C++ classes with virtual (and pure virtual) methods can be extended in Python |
|||
|
|||
Goodies |
|||
******* |
|||
In addition to the core functionality, pybind11 provides some extra goodies: |
|||
|
|||
- It is possible to bind C++11 lambda functions with captured variables. The |
|||
lambda capture data is stored inside the resulting Python function object. |
|||
|
|||
- pybind11 uses C++11 move constructors and move assignment operators whenever |
|||
possible to efficiently transfer custom data types. |
|||
|
|||
- It's easy to expose the internal storage of custom data types through |
|||
Pythons' buffer protocols. This is handy e.g. for fast conversion between |
|||
C++ matrix classes like Eigen and NumPy without expensive copy operations. |
|||
|
|||
- pybind11 can automatically vectorize functions so that they are transparently |
|||
applied to all entries of one or more NumPy array arguments. |
|||
|
|||
- Python's slice-based access and assignment operations can be supported with |
|||
just a few lines of code. |
|||
|
|||
- Everything is contained in just a few header files; there is no need to link |
|||
against any additional libraries. |
|||
|
|||
- Binaries are generally smaller by a factor of 2 or more compared to |
|||
equivalent bindings generated by Boost.Python. |
|||
|
|||
- When supported by the compiler, two new C++14 features (relaxed constexpr and |
|||
return value deduction) are used to precompute function signatures at compile |
|||
time, leading to smaller binaries. |
|||
|
|||
- With little extra effort, C++ types can be pickled and unpickled similar to |
|||
regular Python objects. |
|||
|
|||
Supported compilers |
|||
******************* |
|||
|
|||
1. Clang/LLVM (any non-ancient version with C++11 support) |
|||
2. GCC (any non-ancient version with C++11 support) |
|||
3. Microsoft Visual Studio 2015 or newer |
|||
4. Intel C++ compiler v15 or newer |
|||
@ -1,23 +0,0 @@ |
|||
Limitations |
|||
########### |
|||
|
|||
pybind11 strives to be a general solution to binding generation, but it also has |
|||
certain limitations: |
|||
|
|||
- pybind11 casts away ``const``-ness in function arguments and return values. |
|||
This is in line with the Python language, which has no concept of ``const`` |
|||
values. This means that some additional care is needed to avoid bugs that |
|||
would be caught by the type checker in a traditional C++ program. |
|||
|
|||
- Multiple inheritance relationships on the C++ side cannot be mapped to |
|||
Python. |
|||
|
|||
- The NumPy interface ``pybind11::array`` greatly simplifies accessing |
|||
numerical data from C++ (and vice versa), but it's not a full-blown array |
|||
class like ``Eigen::Array`` or ``boost.multi_array``. |
|||
|
|||
All of these features could be implemented but would lead to a significant |
|||
increase in complexity. I've decided to draw the line here to keep this project |
|||
simple and compact. Users who absolutely require these features are encouraged |
|||
to fork pybind11. |
|||
|
|||
|
Before Width: 1131 | Height: 336 | Size: 57 KiB |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue