From c7c29daf5e171fa8a95d9d38241aecf118a2689c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 23 Oct 2023 20:16:01 -0400 Subject: [PATCH 1/7] initial implementation --- CMakeLists.txt | 13 ++++ README.md | 77 ++++++++++++++++++++++++ cmake/xo_pydistributionConfig.cmake.in | 4 ++ include/README.md | 1 + src/pydistribution/CMakeLists.txt | 7 +++ src/pydistribution/pydistribution.cpp | 74 +++++++++++++++++++++++ src/pydistribution/pydistribution.hpp.in | 25 ++++++++ 7 files changed, 201 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 cmake/xo_pydistributionConfig.cmake.in create mode 100644 include/README.md create mode 100644 src/pydistribution/CMakeLists.txt create mode 100644 src/pydistribution/pydistribution.cpp create mode 100644 src/pydistribution/pydistribution.hpp.in diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..d2823501 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +# xo-pydistribution/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_pydistribution VERSION 1.0) + +include(xo_macros/xo-project-macros) + +xo_cxx_toplevel_options() + +add_subdirectory(src/pydistribution) + +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) diff --git a/README.md b/README.md new file mode 100644 index 00000000..ef597486 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# python bindings for c++ reflection library (xo-distribution) + +## Getting Started + +### build + install dependencies + +- [github/Rconybea/xo-pyutil](https://github.com/Rconybea/xo-pyutil) +- [github/Rconybea/xo-reflect](https://github.com/Rconybea/xo-distribution) + +### build + install +``` +$ cd xo-pydistribution +$ mkdir build +$ cd build +$ INSTALL_PREFIX=/usr/local # or wherever you prefer, e.g. ~/local +$ cmake \ + -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake \ + -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. +$ make +$ make install +``` +(also see .github/workflows/main.yml) + +### build for unit test coverage +``` +$ cd xo-pydistribution +$ mkdir build-ccov +$ cd build-ccov +$ cmake \ + -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake \ + -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} \ + -DCODE_COVERAGE=ON \ + -DCMAKE_BUILD_TYPE=Debug .. +``` + +### LSP (language server) support + +LSP looks for compile commands in the root of the source tree; +while Cmake creates them in the root of its build directory. + +``` +$ cd xo-pydistribution +$ ln -s build/compile_commands.json # supply compile commands to LSP +``` + +## Examples + +Assumes `xo-pydistribution` installed to `~/local2/lib` + +``` +PYTHONPATH=~/local2/lib python +>>> import pydistribution +>>> dir(pydistribution) +['Distribution', 'ExplicitDist', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'normalcdf'] +>>> from pydistribution import * +``` + +normal distribution +``` +>>> normalcdf(0.0) +0.5 +>>> normalcdf(3.0) +0.9986501019683699 +``` + +explicit distribution (online implementation). +intended to model empirically a Bayesian prior. +``` +>>> d=ExplicitDist.make(bucket_dx=0.01, ref_value=1e-6) +>>> d +]"> +>>> d.cdf(0.0) +0.0 +>>> d.cdf(0.01) +1.0 +``` diff --git a/cmake/xo_pydistributionConfig.cmake.in b/cmake/xo_pydistributionConfig.cmake.in new file mode 100644 index 00000000..9c15f36a --- /dev/null +++ b/cmake/xo_pydistributionConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/include/README.md b/include/README.md new file mode 100644 index 00000000..ec349995 --- /dev/null +++ b/include/README.md @@ -0,0 +1 @@ +placeholder for future pydistribution #include files diff --git a/src/pydistribution/CMakeLists.txt b/src/pydistribution/CMakeLists.txt new file mode 100644 index 00000000..a050be15 --- /dev/null +++ b/src/pydistribution/CMakeLists.txt @@ -0,0 +1,7 @@ +# xo_pydistribution/src/pydistribution/CMakeLists.txt + +set(SELF_LIB pydistribution) +set(SELF_SRCS pydistribution.cpp) + +xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) +xo_pybind11_dependency(${SELF_LIB} xo_distribution) diff --git a/src/pydistribution/pydistribution.cpp b/src/pydistribution/pydistribution.cpp new file mode 100644 index 00000000..5d107233 --- /dev/null +++ b/src/pydistribution/pydistribution.cpp @@ -0,0 +1,74 @@ +/* @file pydistribution.cpp */ + +#include "pydistribution.hpp" +#include "xo/distribution/Normal.hpp" +#include "xo/distribution/ExplicitDist.hpp" +#include "xo/reflect/SelfTagging.hpp" +#include "xo/pyutil/pyutil.hpp" +#include +#include + +namespace xo { + using xo::distribution::Normal; + using xo::distribution::Distribution; + using xo::distribution::ExplicitDist; + using xo::ref::rp; + + namespace sim { + namespace py = pybind11; + + PYBIND11_MODULE(PYDISTRIBUTION_MODULE_NAME(), m) { + m.doc() = "pybind11 distribution plugin"; // optional module docstring + + m.def("normalcdf", + &Normal::cdf_impl, + "cumulative normal distribution", + py::arg("x")); + + py::class_, + rp>>(m, "Distribution") + .def("cdf", &Distribution::cdf, + "return cumulative distribution function at x", + py::arg("x")); + + py::class_, + Distribution, + rp>>(m, "ExplicitDist") + .def_static("make", &ExplicitDist::make, + "create instance", + py::arg("bucket_dx"), py::arg("ref_value")) + .def_static("make_n", &ExplicitDist::make_n, + "create instance with n buckets", + py::arg("n"), py::arg("bucket_dx"), py::arg("ref_value")) + .def("n_bucket", &ExplicitDist::n_bucket, + "return number of explicitly-represented buckets in distribution") + .def("lo", &ExplicitDist::lo, + "return least upper bound x: cdf(x)=0") + .def("hi", &ExplicitDist::hi, + "return greatest lower bound x: cdf(x)=1") + .def("density", &ExplicitDist::density, + "return probability density at x", + py::arg("x")) + .def("density_v", &ExplicitDist::density_v, + "return probability density vector for all explicit buckets." + " each member is pair {lh bucket edge, density}") + .def("signed_bucket_index", &ExplicitDist::signed_bucket_index, + "signed index to probability bucket. ref_value -> 0", + py::arg("x")) + .def("scale_bucket", &ExplicitDist::scale_bucket, + "scale probability weight in bucket containing x by k", + py::arg("x"), py::arg("k")) + .def("scale_by_normal_cdf", &ExplicitDist::scale_by_normal_cdf, + "scale by normal cumulative distribution N(sign.(x-mean)/sigma)." + " expect sign in {+1, -1}", + py::arg("sign"), py::arg("mean"), py::arg("sigma")) + .def("renormalize", &ExplicitDist::renormalize, + "renormalize to ensure sum of weights=1") + .def("check_renormalize", &ExplicitDist::check_renormalize, + "renormalize if needed, otherwise do nothing") + .def("__repr__", &ExplicitDist::display_string); + } + } /*namespace sim*/ +} /*namespace xo*/ + +/* end pydistribution.cpp */ diff --git a/src/pydistribution/pydistribution.hpp.in b/src/pydistribution/pydistribution.hpp.in new file mode 100644 index 00000000..bf94e785 --- /dev/null +++ b/src/pydistribution/pydistribution.hpp.in @@ -0,0 +1,25 @@ +/* @file pydistribution.hpp + * + * automatically generated from src/pydistribution/pydistribution.hpp.in + * see src/pydistribution/CMakeLists.txt + */ + +/* python requires module name = library name + * example: + * PYBIND11_MODULE(PYDISTRIBUTION_MODULE_NAME(), m) { ... } + */ +#define PYDISTRIBUTION_MODULE_NAME() @SELF_LIB@ + +/* example: + * py::module_::import(PYDISTRIBUTION_MODULE_NAME_STR) + */ +#define PYDISTRIBUTION_MODULE_NAME_STR "@SELF_LIB@" + +/* example: + * PYDISTRIBUTION_IMPORT_MODULE() + * replaces + * py::module_::import("pydistribution") + */ +#define PYDISTRIBUTION_IMPORT_MODULE() py::module_::import("@SELF_LIB@") + +/* end pydistribution.hpp */ From 026560c4272b7794de73451b1bf350dd04f1ac4b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 23 Oct 2023 20:28:52 -0400 Subject: [PATCH 2/7] bugfix: compile fixes --- src/pydistribution/CMakeLists.txt | 1 + src/pydistribution/pydistribution.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pydistribution/CMakeLists.txt b/src/pydistribution/CMakeLists.txt index a050be15..8adaad7e 100644 --- a/src/pydistribution/CMakeLists.txt +++ b/src/pydistribution/CMakeLists.txt @@ -5,3 +5,4 @@ set(SELF_SRCS pydistribution.cpp) xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) xo_pybind11_dependency(${SELF_LIB} xo_distribution) +xo_pybind11_dependency(${SELF_LIB} xo_pyutil) diff --git a/src/pydistribution/pydistribution.cpp b/src/pydistribution/pydistribution.cpp index 5d107233..8104ccaf 100644 --- a/src/pydistribution/pydistribution.cpp +++ b/src/pydistribution/pydistribution.cpp @@ -3,7 +3,7 @@ #include "pydistribution.hpp" #include "xo/distribution/Normal.hpp" #include "xo/distribution/ExplicitDist.hpp" -#include "xo/reflect/SelfTagging.hpp" +//#include "xo/reflect/SelfTagging.hpp" #include "xo/pyutil/pyutil.hpp" #include #include From aa539e1075a9d612ad7028f2b39f462e8ffcdfd0 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 24 Oct 2023 22:15:43 -0400 Subject: [PATCH 3/7] + .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d6536bad --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +compile_commands.json From b07ce785c5d51c6aa9a0d452dab674f5ee1c944f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 15 Mar 2024 19:31:30 -0400 Subject: [PATCH 4/7] build: streamline CMAKE_MODULE_PATH interaction --- .gitignore | 6 +++++- CMakeLists.txt | 2 +- README.md | 6 +++--- cmake/xo-bootstrap-macros.cmake | 12 ++++++++++++ src/pydistribution/CMakeLists.txt | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 cmake/xo-bootstrap-macros.cmake diff --git a/.gitignore b/.gitignore index d6536bad..13c0afb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ -build +# clangd working space (see emacs+lsp) +.cache +# typical cmake build directory (source-tree-nephew) +.build* +# symlink to builddir/compile_commands.json; should be set manually in dev sandbox compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index d2823501..7b4de072 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.10) project(xo_pydistribution VERSION 1.0) -include(xo_macros/xo-project-macros) +include(cmake/xo-bootstrap-macros.cmake) xo_cxx_toplevel_options() diff --git a/README.md b/README.md index ef597486..98199fef 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,10 @@ Assumes `xo-pydistribution` installed to `~/local2/lib` ``` PYTHONPATH=~/local2/lib python ->>> import pydistribution ->>> dir(pydistribution) +>>> import xo_pydistribution +>>> dir(xo_pydistribution) ['Distribution', 'ExplicitDist', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'normalcdf'] ->>> from pydistribution import * +>>> from xo_pydistribution import * ``` normal distribution diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..16644435 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,12 @@ +if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix")) + # default to typical install location for xo-project-macros + set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) +endif() + +message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") +message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo-project-macros) diff --git a/src/pydistribution/CMakeLists.txt b/src/pydistribution/CMakeLists.txt index 8adaad7e..356ba92e 100644 --- a/src/pydistribution/CMakeLists.txt +++ b/src/pydistribution/CMakeLists.txt @@ -1,6 +1,6 @@ # xo_pydistribution/src/pydistribution/CMakeLists.txt -set(SELF_LIB pydistribution) +set(SELF_LIB xo_pydistribution) set(SELF_SRCS pydistribution.cpp) xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) From 5a45859f5167c625b225774efe81e376558a08de Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 29 Mar 2024 14:33:41 -0400 Subject: [PATCH 5/7] build: suppress bootstrap message in submodule build --- cmake/xo-bootstrap-macros.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake index 16644435..96592216 100644 --- a/cmake/xo-bootstrap-macros.cmake +++ b/cmake/xo-bootstrap-macros.cmake @@ -3,8 +3,10 @@ if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "pr set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) endif() -message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") -message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +if (NOT XO_SUBMODULE_BUILD) + message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") + message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +endif() # needs to have been installed somewhere on CMAKE_MODULE_PATH, # (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) From 07be5ae7ec78f71b63ed21eff71be6ee72fe5cec Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 14 Sep 2024 14:27:07 -0500 Subject: [PATCH 6/7] xo-pydistribution: build: update to latest xo-cmake macros --- CMakeLists.txt | 17 ++++++++++++++++- cmake/xo-bootstrap-macros.cmake | 33 +++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b4de072..3389b1a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,25 @@ cmake_minimum_required(VERSION 3.10) project(xo_pydistribution VERSION 1.0) +include(GNUInstallDirs) include(cmake/xo-bootstrap-macros.cmake) -xo_cxx_toplevel_options() +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings (usually temporary) + +set(PROJECT_CXX_FLAGS "") +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- +# sources add_subdirectory(src/pydistribution) +# ---------------------------------------------------------------- +# provide find_package() support + xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# end CMakeLists.txt diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake index 96592216..aba31169 100644 --- a/cmake/xo-bootstrap-macros.cmake +++ b/cmake/xo-bootstrap-macros.cmake @@ -1,14 +1,35 @@ -if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix")) - # default to typical install location for xo-project-macros - set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") endif() +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + if (NOT XO_SUBMODULE_BUILD) - message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") - message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() endif() # needs to have been installed somewhere on CMAKE_MODULE_PATH, # (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) # -include(xo_macros/xo-project-macros) +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() From a142a0044c75984848735fbfdc24e6dfb88d01cc Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 14 Sep 2024 14:27:22 -0500 Subject: [PATCH 7/7] xo-pydistribution: bugfix: xo::ref::rp -> xo::rp --- src/pydistribution/CMakeLists.txt | 2 ++ src/pydistribution/pydistribution.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pydistribution/CMakeLists.txt b/src/pydistribution/CMakeLists.txt index 356ba92e..389ae251 100644 --- a/src/pydistribution/CMakeLists.txt +++ b/src/pydistribution/CMakeLists.txt @@ -3,6 +3,8 @@ set(SELF_LIB xo_pydistribution) set(SELF_SRCS pydistribution.cpp) +# ---------------------------------------------------------------- + xo_pybind11_library(${SELF_LIB} ${PROJECT_NAME}Targets ${SELF_SRCS}) xo_pybind11_dependency(${SELF_LIB} xo_distribution) xo_pybind11_dependency(${SELF_LIB} xo_pyutil) diff --git a/src/pydistribution/pydistribution.cpp b/src/pydistribution/pydistribution.cpp index 8104ccaf..58793950 100644 --- a/src/pydistribution/pydistribution.cpp +++ b/src/pydistribution/pydistribution.cpp @@ -12,7 +12,6 @@ namespace xo { using xo::distribution::Normal; using xo::distribution::Distribution; using xo::distribution::ExplicitDist; - using xo::ref::rp; namespace sim { namespace py = pybind11;