diff --git a/science/py-dwave-optimization/Makefile b/science/py-dwave-optimization/Makefile index 813a2a6d4546..726b128a01be 100644 --- a/science/py-dwave-optimization/Makefile +++ b/science/py-dwave-optimization/Makefile @@ -1,42 +1,47 @@ PORTNAME= dwave-optimization -DISTVERSION= 0.7.2 +DISTVERSION= 0.7.3 CATEGORIES= science python # quantum-computing MASTER_SITES= PYPI PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX} DISTNAME= ${PORTNAME:S/-/_/}-${DISTVERSION} MAINTAINER= yuri@FreeBSD.org COMMENT= DWave: Nonlinear models for industrial optimization problems WWW= https://github.com/dwavesystems/dwave-optimization LICENSE= APACHE20 LICENSE_FILE= ${WRKSRC}/LICENSE -BUILD_DEPENDS= ${PYTHON_PKGNAMEPREFIX}meson-python>0:devel/meson-python@${PY_FLAVOR} \ - ${PYTHON_PKGNAMEPREFIX}numpy>=1.21.3:math/py-numpy@${PY_FLAVOR} -RUN_DEPENDS= ${PYTHON_PKGNAMEPREFIX}numpy>=1.21.3:math/py-numpy@${PY_FLAVOR} -TEST_DEPENDS= ${PYTHON_PKGNAMEPREFIX}dimod>=0.12.21:science/py-dimod@${PY_FLAVOR} \ - ${PYTHON_PKGNAMEPREFIX}scikit-learn>=1.6.0:science/py-scikit-learn@${PY_FLAVOR} +BUILD_DEPENDS= ${PYTHON_PKGNAMEPREFIX}cython>=3.2.0:lang/cython@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}meson-python>=0.18.0:devel/meson-python@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}numpy>=2:math/py-numpy@${PY_FLAVOR} \ + meson>=1.10.2:devel/meson \ + ninja>=1.13.0:devel/ninja +RUN_DEPENDS= ${PYTHON_PKGNAMEPREFIX}numpy>=2:math/py-numpy@${PY_FLAVOR} +TEST_DEPENDS= ${PYTHON_PKGNAMEPREFIX}dimod>=0.12.16:science/py-dimod@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}networkx>=3.2:math/py-networkx@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}scikit-learn>=1.6.0:science/py-scikit-learn@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}scipy>=1.13.0:science/py-scipy@${PY_FLAVOR} USES= python USE_PYTHON= autoplist cython pep517 pytest PYTEST_BROKEN_TESTS= test_simple_inputs post-install: # strip binaries @${STRIP_CMD} \ ${STAGEDIR}${PYTHON_SITELIBDIR}/dwave/optimization/_model${PYTHON_TAG}.so \ ${STAGEDIR}${PYTHON_SITELIBDIR}/dwave/optimization/_utilities${PYTHON_TAG}.so \ ${STAGEDIR}${PYTHON_SITELIBDIR}/dwave/optimization/libdwave-optimization.so \ ${STAGEDIR}${PYTHON_SITELIBDIR}/dwave/optimization/states${PYTHON_TAG}.so pre-test: @cd ${STAGEDIR}${PYTHON_SITELIBDIR}/dwave/optimization && \ ${FIND} . -name '*.so' -exec ${CP} {} ${WRKSRC}/dwave/optimization/{} \; -# tests as of 0.7.2: 869 passed, 1 broken in 3.23s +# tests as of 0.7.3: 874 passed, 5 deselected, 538 subtests passed in 3.39s # all C++ patches are workaround for https://github.com/dwavesystems/dwave-optimization/issues/595 .include diff --git a/science/py-dwave-optimization/distinfo b/science/py-dwave-optimization/distinfo index a2e87c68b9ae..ec6b27725fb9 100644 --- a/science/py-dwave-optimization/distinfo +++ b/science/py-dwave-optimization/distinfo @@ -1,3 +1,3 @@ -TIMESTAMP = 1785188115 -SHA256 (dwave_optimization-0.7.2.tar.gz) = c8bd908c4714a0036a8710ad27840b5226c3b3cdd9f85f5176149b7db3a4ace3 -SIZE (dwave_optimization-0.7.2.tar.gz) = 535092 +TIMESTAMP = 1788794378 +SHA256 (dwave_optimization-0.7.3.tar.gz) = 2a610790c1a329bdc85e0db0c8d6e625f660969cd1f66cfd43a37d5b6f25fa7e +SIZE (dwave_optimization-0.7.3.tar.gz) = 549273 diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization__model.pyx b/science/py-dwave-optimization/files/patch-dwave_optimization__model.pyx deleted file mode 100644 index 352cb53479e2..000000000000 --- a/science/py-dwave-optimization/files/patch-dwave_optimization__model.pyx +++ /dev/null @@ -1,56 +0,0 @@ --- Use the mangled C++ type name as the runtime type registry key instead of --- std::type_index. On FreeBSD/Clang libc++ compares type_info object addresses --- by default, and the same class template instantiations are emitted into both --- libdwave-optimization.so and each Cython extension module. The resulting --- distinct type_info objects for the same type cause registry lookups to fail, --- producing "given pointer cannot be cast to a known node type". Comparing --- mangled type name strings works across DSO boundaries. - ---- dwave/optimization/_model.pyx.orig 2026-07-23 12:58:12 UTC -+++ dwave/optimization/_model.pyx -@@ -32,7 +32,7 @@ - from libcpp cimport bool - from libcpp.memory cimport make_shared - from libcpp.span cimport span --from libcpp.typeindex cimport type_index -+from libcpp.string cimport string - from libcpp.unordered_map cimport unordered_map - from libcpp.utility cimport move - from libcpp.vector cimport vector -@@ -65,17 +65,22 @@ - """A tuple of 2-tuples listing all serialization versions supported.""" - - --# Store a mapping from the type_index of each C++ Node type to the relevant --# Cython class. We don't refcount the PyObject*s pointing to each class because --# the lifespace of this map is identical to that of the type objects. --cdef unordered_map[type_index, PyObject*] _cpp_type_to_python -+# Store a mapping from the mangled C++ type name of each Node type to the -+# relevant Cython class. We use the type name string rather than std::type_index -+# because libc++'s default type_info comparison compares object addresses, and -+# the same C++ class template instantiations are emitted into both -+# libdwave-optimization.so and the individual Cython extension modules, giving -+# distinct type_info objects for the same type across DSOs on FreeBSD/Clang. -+# We don't refcount the PyObject*s pointing to each class because the lifespan of -+# this map is identical to that of the type objects. -+cdef unordered_map[string, PyObject*] _cpp_type_to_python - - # Register a mapping between the given Cython class and C++ class. - cdef void _register(object cls, const type_info& typeinfo): - """Register a Python/Cython symbol to allow it to be created from a pointer - via `symbol_from_ptr`. - """ -- _cpp_type_to_python[type_index(typeinfo)] = (cls) -+ _cpp_type_to_python[string(typeinfo.name())] = (cls) - - - cdef object symbol_from_ptr(_Graph model, cppNode* node_ptr): -@@ -86,7 +91,7 @@ - raise ValueError("cannot construct a Symbol from the given pointer") - - try: -- cls = _cpp_type_to_python.at(type_index(typeid(deref(node_ptr)))) -+ cls = _cpp_type_to_python.at(string(typeid(deref(node_ptr)).name())) - except IndexError: - # IndexError would be returned by .at() - raise RuntimeError("given pointer cannot be cast to a known node type") from None diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_binaryop.hpp b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_binaryop.hpp deleted file mode 100644 index 6f107c0cecb0..000000000000 --- a/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_binaryop.hpp +++ /dev/null @@ -1,34 +0,0 @@ --- Declare explicit template instantiations for BinaryOpNode specializations so --- they are defined only in libdwave-optimization.so (dwave/optimization/src/nodes/binaryop.cpp). --- Without this, each Cython extension module that creates a binary-op node --- instantiates its own copy of the class template, producing a distinct type_info --- object for the same type. libc++'s default type_info comparison then treats --- those copies as different types and cross-DSO dynamic_cast fails on --- FreeBSD/Clang. - ---- dwave/optimization/include/dwave-optimization/nodes/binaryop.hpp.orig 2026-07-23 12:58:12 UTC -+++ dwave/optimization/include/dwave-optimization/nodes/binaryop.hpp -@@ -106,4 +106,23 @@ - using SubtractNode = BinaryOpNode>; - using XorNode = BinaryOpNode>; - -+// Explicit instantiations live in dwave/optimization/src/nodes/binaryop.cpp so -+// that libdwave-optimization.so owns the canonical type_info objects. Tell -+// other translation units (especially the Cython extension modules) not to -+// emit their own copies, which would break cross-DSO dynamic_cast on -+// FreeBSD/Clang. -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+extern template class BinaryOpNode>; -+ - } // namespace dwave::optimization diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_lp.hpp b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_lp.hpp index 55123382dbfd..9aca0bf0b9fb 100644 --- a/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_lp.hpp +++ b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_lp.hpp @@ -1,53 +1,54 @@ --- Make the LP node destructors out-of-line so their type_info objects are --- emitted as strong (GLOBAL) symbols in libdwave-optimization.so. Without --- this, the Cython lp.so module emits its own copies of the polymorphic base --- LinearProgramNodeBase type_info, which breaks cross-DSO dynamic_cast from --- LinearProgramNode to LinearProgramNodeBase on FreeBSD/Clang. +-- Add missing out-of-line destructor declarations for the LP node classes so +-- their type_info objects are emitted as strong (GLOBAL) symbols in +-- libdwave-optimization.so. Without this, the Cython lp.so module emits its +-- own copies of the polymorphic base LinearProgramNodeBase type_info, which +-- breaks cross-DSO dynamic_cast from LinearProgramNode to LinearProgramNodeBase +-- on FreeBSD/Clang. ---- dwave/optimization/include/dwave-optimization/nodes/lp.hpp.orig 2026-07-23 12:58:12 UTC +--- dwave/optimization/include/dwave-optimization/nodes/lp.hpp.orig 2026-09-07 15:29:23 UTC +++ dwave/optimization/include/dwave-optimization/nodes/lp.hpp -@@ -27,6 +27,8 @@ +@@ -27,6 +27,8 @@ class LinearProgramFeasibleNode : public ScalarOutputM /// A logical node that propagates whether or not its predecessor LinearProgram is feasible. class LinearProgramFeasibleNode : public ScalarOutputMixin, true> { public: + virtual ~LinearProgramFeasibleNode(); + explicit LinearProgramFeasibleNode(LinearProgramNodeBase* lp_ptr); - + /// @copydoc Node::initialize_state() -@@ -53,6 +55,8 @@ - +@@ -53,6 +55,8 @@ class LinearProgramNodeBase : public Node { + class LinearProgramNodeBase : public Node { public: + virtual ~LinearProgramNodeBase(); + /// The default lower bound for variables static const double default_lower_bound(); - -@@ -102,6 +106,8 @@ + +@@ -102,6 +106,8 @@ class LinearProgramNode : public EqualityMixin { public: + virtual ~LinearProgramNode(); + - /// Construct a LinearProgramNode - /// - /// Note: parameter names are chosen to match scipy.optimize.lingprog() -@@ -194,6 +200,8 @@ + using linprog_type = std::function( + std::span c, + std::span b_lb, +@@ -208,6 +214,8 @@ class LinearProgramObjectiveValueNode : public ScalarO /// LinearProgramNode. Note that the output is undefined if the solution is not feasible. class LinearProgramObjectiveValueNode : public ScalarOutputMixin, true> { public: + virtual ~LinearProgramObjectiveValueNode(); + explicit LinearProgramObjectiveValueNode(LinearProgramNodeBase* lp_ptr); - + /// @copydoc Node::initialize_state() -@@ -222,6 +230,8 @@ +@@ -236,6 +244,8 @@ class LinearProgramSolutionNode : public ArrayOutputMi /// solution may not be feasible or optimial. class LinearProgramSolutionNode : public ArrayOutputMixin> { public: + virtual ~LinearProgramSolutionNode(); + explicit LinearProgramSolutionNode(LinearProgramNodeBase* lp_ptr); - + /// @copydoc Array::buff()