commit c7c29daf5e171fa8a95d9d38241aecf118a2689c Author: Roland Conybeare Date: Mon Oct 23 20:16:01 2023 -0400 initial implementation 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 */