git: c4f095e2f2bb - main - science/py-dwave-optimization: update 0.7.1 → 0.7.2
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 28 Jul 2026 08:23:22 UTC
The branch main has been updated by yuri:
URL: https://cgit.FreeBSD.org/ports/commit/?id=c4f095e2f2bb6cc8e3511ef2827fc7675227b869
commit c4f095e2f2bb6cc8e3511ef2827fc7675227b869
Author: Yuri Victorovich <yuri@FreeBSD.org>
AuthorDate: 2026-07-28 03:59:50 +0000
Commit: Yuri Victorovich <yuri@FreeBSD.org>
CommitDate: 2026-07-28 08:23:03 +0000
science/py-dwave-optimization: update 0.7.1 → 0.7.2
---
science/py-dwave-optimization/Makefile | 23 ++++++---
science/py-dwave-optimization/distinfo | 6 +--
.../files/patch-dwave_optimization__model.pyx | 56 ++++++++++++++++++++++
...timization_include_dwave-optimization_array.hpp | 10 ++++
...timization_include_dwave-optimization_graph.hpp | 23 +++++++++
...n_include_dwave-optimization_nodes_binaryop.hpp | 34 +++++++++++++
...ization_include_dwave-optimization_nodes_lp.hpp | 53 ++++++++++++++++++++
...ion_include_dwave-optimization_nodes_naryop.hpp | 20 ++++++++
...ion_include_dwave-optimization_nodes_reduce.hpp | 22 +++++++++
...on_include_dwave-optimization_nodes_unaryop.hpp | 29 +++++++++++
.../files/patch-dwave_optimization_src_graph.cpp | 13 +++++
.../patch-dwave_optimization_src_nodes_lp.cpp | 20 ++++++++
.../py-dwave-optimization/files/patch-meson.build | 28 +++++++++++
13 files changed, 328 insertions(+), 9 deletions(-)
diff --git a/science/py-dwave-optimization/Makefile b/science/py-dwave-optimization/Makefile
index 6c9df0b43303..813a2a6d4546 100644
--- a/science/py-dwave-optimization/Makefile
+++ b/science/py-dwave-optimization/Makefile
@@ -1,6 +1,5 @@
PORTNAME= dwave-optimization
-DISTVERSION= 0.7.1
-PORTREVISION= 3
+DISTVERSION= 0.7.2
CATEGORIES= science python # quantum-computing
MASTER_SITES= PYPI
PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX}
@@ -13,19 +12,31 @@ WWW= https://github.com/dwavesystems/dwave-optimization
LICENSE= APACHE20
LICENSE_FILE= ${WRKSRC}/LICENSE
-BUILD_DEPENDS= ${PYTHON_PKGNAMEPREFIX}numpy>=1.16:math/py-numpy@${PY_FLAVOR} \
- ${PYTHON_PKGNAMEPREFIX}meson-python>=0.18.0:devel/meson-python@${PY_FLAVOR}
-RUN_DEPENDS= ${PYTHON_PKGNAMEPREFIX}numpy>=1.16:math/py-numpy@${PY_FLAVOR}
+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}
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 \
+ ${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
+
+# all C++ patches are workaround for https://github.com/dwavesystems/dwave-optimization/issues/595
.include <bsd.port.mk>
diff --git a/science/py-dwave-optimization/distinfo b/science/py-dwave-optimization/distinfo
index ffa3fe497467..a2e87c68b9ae 100644
--- a/science/py-dwave-optimization/distinfo
+++ b/science/py-dwave-optimization/distinfo
@@ -1,3 +1,3 @@
-TIMESTAMP = 1781738751
-SHA256 (dwave_optimization-0.7.1.tar.gz) = 70085ae160b7692225392650924f137bc2c74f405d1a45d4e011aa647cb3ec03
-SIZE (dwave_optimization-0.7.1.tar.gz) = 520638
+TIMESTAMP = 1785188115
+SHA256 (dwave_optimization-0.7.2.tar.gz) = c8bd908c4714a0036a8710ad27840b5226c3b3cdd9f85f5176149b7db3a4ace3
+SIZE (dwave_optimization-0.7.2.tar.gz) = 535092
diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization__model.pyx b/science/py-dwave-optimization/files/patch-dwave_optimization__model.pyx
new file mode 100644
index 000000000000..352cb53479e2
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization__model.pyx
@@ -0,0 +1,56 @@
+-- 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)] = <PyObject*>(cls)
++ _cpp_type_to_python[string(typeinfo.name())] = <PyObject*>(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 = <object>_cpp_type_to_python.at(type_index(typeid(deref(node_ptr))))
++ cls = <object>_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_array.hpp b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_array.hpp
new file mode 100644
index 000000000000..f56826b81f7c
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_array.hpp
@@ -0,0 +1,10 @@
+--- dwave/optimization/include/dwave-optimization/array.hpp.orig 2026-07-27 22:30:07 UTC
++++ dwave/optimization/include/dwave-optimization/array.hpp
+@@ -371,6 +371,7 @@ class Array {
+ /// the Array::dynamic() method is provided.
+ class Array {
+ public:
++ virtual ~Array();
+ /// A std::random_access_iterator over the values in the array.
+ using iterator = BufferIterator<double, double>;
+
diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_graph.hpp b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_graph.hpp
new file mode 100644
index 000000000000..a942ca5ccb89
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_graph.hpp
@@ -0,0 +1,23 @@
+--- dwave/optimization/include/dwave-optimization/graph.hpp.orig 2026-07-27 22:30:07 UTC
++++ dwave/optimization/include/dwave-optimization/graph.hpp
+@@ -254,7 +254,7 @@ class Node {
+ };
+
+ Node() noexcept : expired_ptr_(new bool(false)) {}
+- virtual ~Node() { *expired_ptr_ = true; }
++ virtual ~Node();
+
+ // Nodes cannot be moved or copied.
+ Node(const Node&) = delete;
+@@ -452,7 +452,10 @@ NodeType* Graph::emplace_node(Args&&... args) {
+ return ptr; // return the observing pointer
+ }
+
+-class ArrayNode : public Array, public virtual Node {};
++class ArrayNode : public Array, public virtual Node {
++ public:
++ ~ArrayNode() override;
++};
+ class DecisionNode : public Decision, public virtual Node {
+ public:
+ /// Decision nodes by definition do not have a deterministic state.
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
new file mode 100644
index 000000000000..6f107c0cecb0
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_binaryop.hpp
@@ -0,0 +1,34 @@
+-- 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<std::minus<double>>;
+ using XorNode = BinaryOpNode<functional::logical_xor<double>>;
+
++// 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<std::plus<double>>;
++extern template class BinaryOpNode<std::logical_and<double>>;
++extern template class BinaryOpNode<std::divides<double>>;
++extern template class BinaryOpNode<std::equal_to<double>>;
++extern template class BinaryOpNode<std::less_equal<double>>;
++extern template class BinaryOpNode<functional::max<double>>;
++extern template class BinaryOpNode<functional::min<double>>;
++extern template class BinaryOpNode<functional::modulus<double>>;
++extern template class BinaryOpNode<std::multiplies<double>>;
++extern template class BinaryOpNode<std::logical_or<double>>;
++extern template class BinaryOpNode<functional::safe_divides<double>>;
++extern template class BinaryOpNode<std::minus<double>>;
++extern template class BinaryOpNode<functional::logical_xor<double>>;
++
+ } // 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
new file mode 100644
index 000000000000..55123382dbfd
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_lp.hpp
@@ -0,0 +1,53 @@
+-- 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.
+
+--- dwave/optimization/include/dwave-optimization/nodes/lp.hpp.orig 2026-07-23 12:58:12 UTC
++++ dwave/optimization/include/dwave-optimization/nodes/lp.hpp
+@@ -27,6 +27,8 @@
+ /// A logical node that propagates whether or not its predecessor LinearProgram is feasible.
+ class LinearProgramFeasibleNode : public ScalarOutputMixin<EqualityMixin<ArrayNode>, true> {
+ public:
++ virtual ~LinearProgramFeasibleNode();
++
+ explicit LinearProgramFeasibleNode(LinearProgramNodeBase* lp_ptr);
+
+ /// @copydoc Node::initialize_state()
+@@ -53,6 +55,8 @@
+
+ class LinearProgramNodeBase : public Node {
+ public:
++ virtual ~LinearProgramNodeBase();
++
+ /// The default lower bound for variables
+ static const double default_lower_bound();
+
+@@ -102,6 +106,8 @@
+ /// callback=None, options=None, x0=None, integrality=None)
+ class LinearProgramNode : public EqualityMixin<LinearProgramNodeBase, LinearProgramNode> {
+ public:
++ virtual ~LinearProgramNode();
++
+ /// Construct a LinearProgramNode
+ ///
+ /// Note: parameter names are chosen to match scipy.optimize.lingprog()
+@@ -194,6 +200,8 @@
+ /// LinearProgramNode. Note that the output is undefined if the solution is not feasible.
+ class LinearProgramObjectiveValueNode : public ScalarOutputMixin<EqualityMixin<ArrayNode>, true> {
+ public:
++ virtual ~LinearProgramObjectiveValueNode();
++
+ explicit LinearProgramObjectiveValueNode(LinearProgramNodeBase* lp_ptr);
+
+ /// @copydoc Node::initialize_state()
+@@ -222,6 +230,8 @@
+ /// solution may not be feasible or optimial.
+ class LinearProgramSolutionNode : public ArrayOutputMixin<EqualityMixin<ArrayNode>> {
+ public:
++ virtual ~LinearProgramSolutionNode();
++
+ explicit LinearProgramSolutionNode(LinearProgramNodeBase* lp_ptr);
+
+ /// @copydoc Array::buff()
diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_naryop.hpp b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_naryop.hpp
new file mode 100644
index 000000000000..0f8d99f6a4e8
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_naryop.hpp
@@ -0,0 +1,20 @@
+-- Declare explicit template instantiations for NaryOpNode specializations so
+-- they are defined only in libdwave-optimization.so (dwave/optimization/src/nodes/naryop.cpp).
+-- This prevents each Cython extension module from emitting its own type_info
+-- copy, which breaks cross-DSO dynamic_cast on FreeBSD/Clang.
+
+--- dwave/optimization/include/dwave-optimization/nodes/naryop.hpp.orig 2026-07-23 12:58:12 UTC
++++ dwave/optimization/include/dwave-optimization/nodes/naryop.hpp
+@@ -86,4 +86,12 @@
+ using NaryMinimumNode = NaryOpNode<functional::min<double>>;
+ using NaryMultiplyNode = NaryOpNode<std::multiplies<double>>;
+
++// Explicit instantiations live in dwave/optimization/src/nodes/naryop.cpp so
++// that libdwave-optimization.so owns the canonical type_info objects. Tell
++// other translation units not to emit their own copies.
++extern template class NaryOpNode<functional::max<double>>;
++extern template class NaryOpNode<functional::min<double>>;
++extern template class NaryOpNode<std::multiplies<double>>;
++extern template class NaryOpNode<std::plus<double>>;
++
+ } // namespace dwave::optimization
diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_reduce.hpp b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_reduce.hpp
new file mode 100644
index 000000000000..5c1f97e00436
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_reduce.hpp
@@ -0,0 +1,22 @@
+-- Declare explicit template instantiations for ReduceNode specializations so
+-- they are defined only in libdwave-optimization.so (dwave/optimization/src/nodes/reduce.cpp).
+-- This prevents each Cython extension module from emitting its own type_info
+-- copy, which breaks cross-DSO dynamic_cast on FreeBSD/Clang.
+
+--- dwave/optimization/include/dwave-optimization/nodes/reduce.hpp.orig 2026-07-23 12:58:12 UTC
++++ dwave/optimization/include/dwave-optimization/nodes/reduce.hpp
+@@ -141,4 +141,14 @@
+ using ProdNode = ReduceNode<std::multiplies<double>>;
+ using SumNode = ReduceNode<std::plus<double>>;
+
++// Explicit instantiations live in dwave/optimization/src/nodes/reduce.cpp so
++// that libdwave-optimization.so owns the canonical type_info objects. Tell
++// other translation units not to emit their own copies.
++extern template class ReduceNode<functional::max<double>>;
++extern template class ReduceNode<functional::min<double>>;
++extern template class ReduceNode<std::logical_and<double>>;
++extern template class ReduceNode<std::logical_or<double>>;
++extern template class ReduceNode<std::multiplies<double>>;
++extern template class ReduceNode<std::plus<double>>;
++
+ } // namespace dwave::optimization
diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_unaryop.hpp b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_unaryop.hpp
new file mode 100644
index 000000000000..45873043e3e2
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_include_dwave-optimization_nodes_unaryop.hpp
@@ -0,0 +1,29 @@
+-- Declare explicit template instantiations for UnaryOpNode specializations so
+-- they are defined only in libdwave-optimization.so (dwave/optimization/src/nodes/unaryop.cpp).
+-- This prevents each Cython extension module from emitting its own type_info
+-- copy, which breaks cross-DSO dynamic_cast on FreeBSD/Clang.
+
+--- dwave/optimization/include/dwave-optimization/nodes/unaryop.hpp.orig 2026-07-23 12:58:12 UTC
++++ dwave/optimization/include/dwave-optimization/nodes/unaryop.hpp
+@@ -96,4 +96,21 @@
+ using SquareRootNode = UnaryOpNode<functional::square_root<double>>;
+ using TanhNode = UnaryOpNode<functional::tanh<double>>;
+
++// Explicit instantiations live in dwave/optimization/src/nodes/unaryop.cpp so
++// that libdwave-optimization.so owns the canonical type_info objects. Tell
++// other translation units not to emit their own copies.
++extern template class UnaryOpNode<functional::abs<double>>;
++extern template class UnaryOpNode<functional::cos<double>>;
++extern template class UnaryOpNode<functional::exp<double>>;
++extern template class UnaryOpNode<functional::expit<double>>;
++extern template class UnaryOpNode<functional::log<double>>;
++extern template class UnaryOpNode<functional::logical<double>>;
++extern template class UnaryOpNode<functional::rint<double>>;
++extern template class UnaryOpNode<functional::sin<double>>;
++extern template class UnaryOpNode<functional::square<double>>;
++extern template class UnaryOpNode<functional::square_root<double>>;
++extern template class UnaryOpNode<functional::tanh<double>>;
++extern template class UnaryOpNode<std::negate<double>>;
++extern template class UnaryOpNode<std::logical_not<double>>;
++
+ } // namespace dwave::optimization
diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization_src_graph.cpp b/science/py-dwave-optimization/files/patch-dwave_optimization_src_graph.cpp
new file mode 100644
index 000000000000..1dba5bb43e27
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_src_graph.cpp
@@ -0,0 +1,13 @@
+--- dwave/optimization/src/graph.cpp.orig 2026-07-27 22:30:07 UTC
++++ dwave/optimization/src/graph.cpp
+@@ -33,6 +33,10 @@ namespace dwave::optimization {
+
+ namespace dwave::optimization {
+
++Node::~Node() { *expired_ptr_ = true; }
++Array::~Array() = default;
++ArrayNode::~ArrayNode() = default;
++
+ void Graph::add_constraint(ArrayNode* constraint_ptr) {
+ if (not constraint_ptr->logical()) {
+ throw std::invalid_argument("constraint must have a logical output");
diff --git a/science/py-dwave-optimization/files/patch-dwave_optimization_src_nodes_lp.cpp b/science/py-dwave-optimization/files/patch-dwave_optimization_src_nodes_lp.cpp
new file mode 100644
index 000000000000..72aae7804fb8
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-dwave_optimization_src_nodes_lp.cpp
@@ -0,0 +1,20 @@
+-- Define out-of-line destructors for the LP node classes so their type_info
+-- objects are emitted as strong (GLOBAL) symbols in libdwave-optimization.so,
+-- matching the header declarations. This fixes cross-DSO dynamic_cast failures
+-- on FreeBSD/Clang where each shared library otherwise gets its own copy of the
+-- base LinearProgramNodeBase type_info.
+
+--- dwave/optimization/src/nodes/lp.cpp.orig 2026-07-23 12:58:12 UTC
++++ dwave/optimization/src/nodes/lp.cpp
+@@ -22,6 +22,12 @@
+ #include "dwave-optimization/graph.hpp"
+
+ namespace dwave::optimization {
++
++LinearProgramFeasibleNode::~LinearProgramFeasibleNode() = default;
++LinearProgramNodeBase::~LinearProgramNodeBase() = default;
++LinearProgramNode::~LinearProgramNode() = default;
++LinearProgramObjectiveValueNode::~LinearProgramObjectiveValueNode() = default;
++LinearProgramSolutionNode::~LinearProgramSolutionNode() = default;
+
+ static constexpr double FEASIBILITY_TOLERANCE = 1e-07;
diff --git a/science/py-dwave-optimization/files/patch-meson.build b/science/py-dwave-optimization/files/patch-meson.build
new file mode 100644
index 000000000000..42ee1f84c6ee
--- /dev/null
+++ b/science/py-dwave-optimization/files/patch-meson.build
@@ -0,0 +1,28 @@
+-- Force libc++ to use name-based type_info comparison. On FreeBSD/Clang the
+-- default compares type_info object addresses, but the same C++ class
+-- template instantiations are emitted into both libdwave-optimization.so and
+-- each Cython extension module, producing distinct type_info objects for the
+-- same type. This breaks cross-DSO std::type_index lookups and dynamic_cast.
+-- The "non-unique" implementation compares mangled type names, so types match
+-- across shared libraries.
+
+--- meson.build.orig 2026-07-23 19:58:12 UTC
++++ meson.build
+@@ -22,6 +22,17 @@ add_project_arguments(['-g1'], language: ['cpp'])
+ # We want some debugging symbols
+ add_project_arguments(['-g1'], language: ['cpp'])
+
++# On FreeBSD/Clang libc++ defaults to comparing type_info object addresses. The
++# same C++ class template instantiations are emitted into both
++# libdwave-optimization.so and the individual Cython extension modules, so
++# each shared object gets its own type_info copy and cross-DSO dynamic_cast
++# and std::type_index lookups fail. Force the "non-unique" implementation,
++# which compares mangled type names, so types match across shared libraries.
++add_project_arguments(
++ '-D_LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION=2',
++ language: 'cpp',
++)
++
+ cpp = meson.get_compiler('cpp')
+ py = import('python').find_installation(pure: false)
+