From 38ad64deeeaf3e9cb452af4a6c5acf9b935af19b Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Tue, 18 Aug 2026 22:11:36 -0700 Subject: [PATCH 1/4] Build Windows wheels with LLVM --- .circleci/config.yml | 9 +++++++-- meson.build | 8 +++++--- releasenotes/notes/c++23-60fb26a150a004e8.yaml | 3 +++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/c++23-60fb26a150a004e8.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index 3693ac0e..bc6d9017 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,6 +7,9 @@ orbs: commands: run-cibuildwheel: parameters: + cibw-config: # environment variables + type: string + default: "" cibw-version: type: string default: 4.2.0 # latest as of August 2026 @@ -16,7 +19,7 @@ commands: shell: bash -eo pipefail command: | python -m pip install --user cibuildwheel==<< parameters.cibw-version >> - python -m cibuildwheel --output-dir dist + << parameters.cibw-config >> python -m cibuildwheel --output-dir dist - store_artifacts: &store-artifacts path: ./dist @@ -122,7 +125,9 @@ jobs: steps: - checkout - - run-cibuildwheel + - run: choco install llvm -y + - run-cibuildwheel: + cibw-config: CIBW_ENVIRONMENT_PASS=CXX CXX=clang-cl python-sdist: docker: diff --git a/meson.build b/meson.build index b8882ec8..cc2c65c1 100644 --- a/meson.build +++ b/meson.build @@ -19,12 +19,14 @@ project( ).stdout(), ) -# We want some debugging symbols -add_project_arguments(['-g1'], language: ['cpp']) - cpp = meson.get_compiler('cpp') py = import('python').find_installation(pure: false) +# We want some debugging symbols +if cpp.has_argument('-g1') + add_project_arguments('-g1', language: 'cpp') +endif + dwave_optimization_include = include_directories('dwave/optimization/include/') dwave_optimization_src = [ 'dwave/optimization/src/nodes/_checkpoints.cpp', diff --git a/releasenotes/notes/c++23-60fb26a150a004e8.yaml b/releasenotes/notes/c++23-60fb26a150a004e8.yaml new file mode 100644 index 00000000..c2b893b0 --- /dev/null +++ b/releasenotes/notes/c++23-60fb26a150a004e8.yaml @@ -0,0 +1,3 @@ +--- +other: + - Build Windows wheels with LLVM rather than MSVC. From 4d548108cc392a27b677c445d888b4f0319e9322 Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Tue, 18 Aug 2026 22:30:16 -0700 Subject: [PATCH 2/4] Compile C++ library with C++23 --- meson.build | 2 +- releasenotes/notes/c++23-60fb26a150a004e8.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index cc2c65c1..1e64bf33 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project( 'dwave-optimization', 'c', 'cpp', 'cython', default_options: [ - 'cpp_std=c++20', + 'cpp_std=c++23', 'buildtype=release', 'b_ndebug=if-release', # add -DNDEBUG when building for "release", which we do by default 'optimization=3', diff --git a/releasenotes/notes/c++23-60fb26a150a004e8.yaml b/releasenotes/notes/c++23-60fb26a150a004e8.yaml index c2b893b0..a76532cb 100644 --- a/releasenotes/notes/c++23-60fb26a150a004e8.yaml +++ b/releasenotes/notes/c++23-60fb26a150a004e8.yaml @@ -1,3 +1,5 @@ --- +features: + - Compile C++ library with C++23. other: - Build Windows wheels with LLVM rather than MSVC. From 2c93f656a7e6a2cf0986ebe039bc92a6a9dae27d Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Tue, 18 Aug 2026 22:32:24 -0700 Subject: [PATCH 3/4] Use std::unreachable() rather than backport --- .../include/dwave-optimization/common.hpp | 13 ------------- .../include/dwave-optimization/iterators.hpp | 3 ++- .../include/dwave-optimization/nodes/testing.hpp | 9 +++++---- dwave/optimization/src/nodes/binaryop.cpp | 11 ++++++----- dwave/optimization/src/nodes/collections.cpp | 2 +- dwave/optimization/src/nodes/creation.cpp | 9 +++++---- dwave/optimization/src/nodes/indexing.cpp | 7 ++++--- dwave/optimization/src/nodes/lambda.cpp | 4 +++- dwave/optimization/src/nodes/lp.cpp | 3 ++- dwave/optimization/src/nodes/naryop.cpp | 5 +++-- dwave/optimization/src/nodes/numbers.cpp | 8 ++++---- dwave/optimization/src/nodes/unaryop.cpp | 6 ++++-- 12 files changed, 39 insertions(+), 41 deletions(-) diff --git a/dwave/optimization/include/dwave-optimization/common.hpp b/dwave/optimization/include/dwave-optimization/common.hpp index bcbf7898..07344982 100644 --- a/dwave/optimization/include/dwave-optimization/common.hpp +++ b/dwave/optimization/include/dwave-optimization/common.hpp @@ -32,16 +32,3 @@ typedef std::int64_t ssize_t; #include // for ssize_t #endif - -namespace dwave::optimization { - -// backport unreachable from c++23 -[[noreturn]] inline void unreachable() { -#if defined(_MSC_VER) && !defined(__clang__) // MSVC - __assume(false); -#else // GCC, Clang - __builtin_unreachable(); -#endif -} - -} // namespace dwave::optimization diff --git a/dwave/optimization/include/dwave-optimization/iterators.hpp b/dwave/optimization/include/dwave-optimization/iterators.hpp index f547459f..4ec2b545 100644 --- a/dwave/optimization/include/dwave-optimization/iterators.hpp +++ b/dwave/optimization/include/dwave-optimization/iterators.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "dwave-optimization/common.hpp" // for ssize_t #include "dwave-optimization/typing.hpp" @@ -461,7 +462,7 @@ requires( case FormatCharacter::signedlonglong_: return *static_cast(ptr); } - unreachable(); + std::unreachable(); } } diff --git a/dwave/optimization/include/dwave-optimization/nodes/testing.hpp b/dwave/optimization/include/dwave-optimization/nodes/testing.hpp index 3954ff67..d079d00e 100644 --- a/dwave/optimization/include/dwave-optimization/nodes/testing.hpp +++ b/dwave/optimization/include/dwave-optimization/nodes/testing.hpp @@ -16,6 +16,7 @@ #include #include +#include #include #include "dwave-optimization/array.hpp" @@ -113,18 +114,18 @@ class DynamicArrayTestingNode : public ArrayOutputMixin, public Decis checkpoint_type& checkpoint ) const override { assert(false and "not implemented"); - unreachable(); + std::unreachable(); } [[noreturn]] void assign_from_checkpoint( State& state, checkpoint_type&& checkpoint ) const override { assert(false and "not implemented"); - unreachable(); + std::unreachable(); } [[noreturn]] virtual checkpoint_type checkpoint(State& state) const override { assert(false and "not implemented"); - unreachable(); + std::unreachable(); } // State mutation methods ************************************************* @@ -185,7 +186,7 @@ class DynamicArrayTestingNode : public ArrayOutputMixin, public Decis case 2: return set(state, rng); default: - unreachable(); + std::unreachable(); } } diff --git a/dwave/optimization/src/nodes/binaryop.cpp b/dwave/optimization/src/nodes/binaryop.cpp index 78ee6535..5f3279b3 100644 --- a/dwave/optimization/src/nodes/binaryop.cpp +++ b/dwave/optimization/src/nodes/binaryop.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "_state.hpp" @@ -151,7 +152,7 @@ std::pair calculate_values_minmax(const Array* lhs_ptr, const Ar } assert(false && "not implemeted yet"); - unreachable(); + std::unreachable(); } template @@ -182,7 +183,7 @@ bool calculate_integral(const Array* lhs_ptr, const Array* rhs_ptr) { } assert(false && "not implemeted yet"); - unreachable(); + std::unreachable(); } template @@ -294,7 +295,7 @@ void BinaryOpNode::initialize_state(State& state) const { } else { // this case is complicated we need to "stretch" dimensions into each other assert(false && "not yet implemented"); - unreachable(); + std::unreachable(); } this->template emplace_data_ptr_(state, std::move(values)); @@ -427,7 +428,7 @@ void BinaryOpNode::propagate(State& state) const { } else { // this case is complicated we need to "stretch" dimensions into eachother assert(false && "not yet implemented"); - unreachable(); + std::unreachable(); } if (ptr->diff().size()) Node::propagate(state); @@ -512,7 +513,7 @@ SizeInfo binaryop_calculate_sizeinfo( // not possible for us to be dynamic and none of our predecessors to be assert(false && "not implemeted"); - unreachable(); + std::unreachable(); } template diff --git a/dwave/optimization/src/nodes/collections.cpp b/dwave/optimization/src/nodes/collections.cpp index bee598ca..099553ff 100644 --- a/dwave/optimization/src/nodes/collections.cpp +++ b/dwave/optimization/src/nodes/collections.cpp @@ -532,7 +532,7 @@ struct DisjointBitSetsNodeData_ : CheckpointableState, NodeStateData { } assert(false and "disjoint set elements must be in exactly one bit-set once"); - unreachable(); + std::unreachable(); } void commit() { diff --git a/dwave/optimization/src/nodes/creation.cpp b/dwave/optimization/src/nodes/creation.cpp index 492ece3a..9d7cf54e 100644 --- a/dwave/optimization/src/nodes/creation.cpp +++ b/dwave/optimization/src/nodes/creation.cpp @@ -14,6 +14,7 @@ #include "dwave-optimization/nodes/creation.hpp" +#include #include #include "_state.hpp" @@ -109,7 +110,7 @@ std::vector arange(const ssize_t start, const ssize_t stop, const ssize_ } } else { assert(false && "0 step not allowed"); - unreachable(); + std::unreachable(); } return arange; @@ -178,7 +179,7 @@ std::pair calculate_values_minmax( } assert(false && "zero step not allowed"); - unreachable(); + std::unreachable(); } const SizeInfo calculate_arange_sizeinfo( @@ -226,7 +227,7 @@ const SizeInfo calculate_arange_sizeinfo( ); } else { assert(false && "unreachable"); - unreachable(); + std::unreachable(); } // Handles all cases EXCEPT the following: "Exactly one predecessor, it @@ -423,7 +424,7 @@ void ARangeNode::propagate(State& state) const { } } else { assert(false && "zero step not allowed"); - unreachable(); + std::unreachable(); } if (ptr->diff().size()) Node::propagate(state); diff --git a/dwave/optimization/src/nodes/indexing.cpp b/dwave/optimization/src/nodes/indexing.cpp index 87988074..47542a91 100644 --- a/dwave/optimization/src/nodes/indexing.cpp +++ b/dwave/optimization/src/nodes/indexing.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "_state.hpp" @@ -781,7 +782,7 @@ void AdvancedIndexingNode::replace_predecessor_(ssize_t index, Node* node_ptr) { } assert(false and "should not be able to get here"); - unreachable(); + std::unreachable(); } void AdvancedIndexingNode::revert(State& state) const { @@ -1405,13 +1406,13 @@ SizeInfo basicindexing_calculate_sizeinfo( } else if (start < 0 && stop >= 0) { // -start:stop:step - imposes a nonlinear maximum size assert(false && "not linear, handled above"); - unreachable(); + std::unreachable(); } else if (start < 0 && stop < 0) { // -start:-stop:step - imposes a maximum size sizeinfo.max = num_per_row * (std::max(stop - start, 0) + step - 1) / step; } else { assert(false && "shouldn't be reachable"); - unreachable(); + std::unreachable(); } } diff --git a/dwave/optimization/src/nodes/lambda.cpp b/dwave/optimization/src/nodes/lambda.cpp index b40ec9a5..75c87561 100644 --- a/dwave/optimization/src/nodes/lambda.cpp +++ b/dwave/optimization/src/nodes/lambda.cpp @@ -14,6 +14,8 @@ #include "dwave-optimization/nodes/lambda.hpp" +#include + #include "_state.hpp" #include "dwave-optimization/array.hpp" #include "dwave-optimization/graph.hpp" @@ -352,7 +354,7 @@ void AccumulateZipNode::propagate(State& state) const { data->emplace_back(val); } else { assert(false && "index is too large for current buffer"); - unreachable(); + std::unreachable(); } } diff --git a/dwave/optimization/src/nodes/lp.cpp b/dwave/optimization/src/nodes/lp.cpp index b1f06f2c..1ab60b83 100644 --- a/dwave/optimization/src/nodes/lp.cpp +++ b/dwave/optimization/src/nodes/lp.cpp @@ -15,6 +15,7 @@ #include "dwave-optimization/nodes/lp.hpp" #include +#include #include "../simplex.hpp" #include "_state.hpp" @@ -433,7 +434,7 @@ void LinearProgramNode::replace_predecessor_(ssize_t index, Node* node_ptr) { if (check_and_replace(lb_ptr_)) return; if (check_and_replace(ub_ptr_)) return; - unreachable(); + std::unreachable(); assert(false and "should never get here"); } diff --git a/dwave/optimization/src/nodes/naryop.cpp b/dwave/optimization/src/nodes/naryop.cpp index 5dc47e48..150337f1 100644 --- a/dwave/optimization/src/nodes/naryop.cpp +++ b/dwave/optimization/src/nodes/naryop.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "_state.hpp" @@ -83,7 +84,7 @@ bool calculate_integral(const std::vector& operands) { } assert(false && "not implemeted yet"); - unreachable(); + std::unreachable(); } template @@ -142,7 +143,7 @@ ValuesInfo calculate_values_info(const std::vector& operands) { } assert(false && "not implemeted yet"); - unreachable(); + std::unreachable(); } template diff --git a/dwave/optimization/src/nodes/numbers.cpp b/dwave/optimization/src/nodes/numbers.cpp index bed90025..a12d667c 100644 --- a/dwave/optimization/src/nodes/numbers.cpp +++ b/dwave/optimization/src/nodes/numbers.cpp @@ -502,7 +502,7 @@ bool satisfies_sum_constraint( break; default: assert(false && "Unexpected operator type."); - unreachable(); + std::unreachable(); } } } @@ -593,7 +593,7 @@ double sum_constraint_delta( return (lhs < bound) ? (bound - lhs) : 0.0; default: assert(false && "Unexpected operator type."); - unreachable(); + std::unreachable(); } } @@ -717,7 +717,7 @@ void NumberNode::initialize_state(State& state) const { initialize_state(state, std::move(values)); } else { assert(false && "Multiple sum constraints not yet supported."); - unreachable(); + std::unreachable(); } } @@ -1608,7 +1608,7 @@ void BinaryNode::initialize_state(State& state) const { initialize_state(state, std::move(values)); } else { assert(false && "Multiple sum constraints not yet supported."); - unreachable(); + std::unreachable(); } } diff --git a/dwave/optimization/src/nodes/unaryop.cpp b/dwave/optimization/src/nodes/unaryop.cpp index 73902761..d871be91 100644 --- a/dwave/optimization/src/nodes/unaryop.cpp +++ b/dwave/optimization/src/nodes/unaryop.cpp @@ -14,6 +14,8 @@ #include "dwave-optimization/nodes/unaryop.hpp" +#include + #include "_state.hpp" namespace dwave::optimization { @@ -58,7 +60,7 @@ std::pair calculate_values_minmax(const Array* array_ptr) { return std::make_pair(low, high); } else if (low >= 0) { assert(false && "min > max"); - unreachable(); + std::unreachable(); } else if (high >= 0) { return std::pair(0.0, std::max(-low, high)); } else { @@ -99,7 +101,7 @@ std::pair calculate_values_minmax(const Array* array_ptr) { } assert(false && "not implemeted yet"); - unreachable(); + std::unreachable(); } template From 4d2a199a9840d32b45c8104ff74e336f1a9e2e34 Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Tue, 18 Aug 2026 22:57:47 -0700 Subject: [PATCH 4/4] Use c++23 features in a few places Specifically places where we have comments wishing for C++23 --- .circleci/config.yml | 2 +- dwave/optimization/src/array.cpp | 2 -- dwave/optimization/src/nodes/flow.cpp | 9 ++------- dwave/optimization/src/nodes/manipulation.cpp | 9 +-------- releasenotes/notes/c++23-60fb26a150a004e8.yaml | 2 ++ 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bc6d9017..a325f692 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -412,7 +412,7 @@ workflows: name: cpp-gcc-<< matrix.gcc-version >> matrix: parameters: - gcc-version: ["12", "latest"] + gcc-version: ["14", "latest"] - cpp-macOS - serialization: requires: diff --git a/dwave/optimization/src/array.cpp b/dwave/optimization/src/array.cpp index 3484d30c..5f897f4d 100644 --- a/dwave/optimization/src/array.cpp +++ b/dwave/optimization/src/array.cpp @@ -241,8 +241,6 @@ std::vector broadcast_shapes( std::vector shape(std::max(lhs.size(), rhs.size())); // Walk backwards through the shapes, checking for dimension compatibility. - // Technically span::rbegin() etc are c++23 features but it seems to work on - // all the compilers we care about. Whereas the c++20 ranges::rbegin() etc do not. auto lit = lhs.rbegin(); const auto lend = lhs.rend(); auto rit = rhs.rbegin(); diff --git a/dwave/optimization/src/nodes/flow.cpp b/dwave/optimization/src/nodes/flow.cpp index 52dc5847..0776410b 100644 --- a/dwave/optimization/src/nodes/flow.cpp +++ b/dwave/optimization/src/nodes/flow.cpp @@ -301,13 +301,8 @@ void WhereNode::initialize_state(State& state) const { std::vector values; values.reserve(condition.size()); - // zip would be very nice here... - for ( - auto cit = condition.begin(), xit = x.begin(), yit = y.begin(); - cit != std::default_sentinel; - ++cit, ++xit, ++yit - ) { - values.emplace_back((*cit) ? *xit : *yit); + for (const auto& [ci, xi, yi] : std::views::zip(condition, x, y)) { + values.emplace_back(ci ? xi : yi); } emplace_data_ptr_(state, std::move(values)); diff --git a/dwave/optimization/src/nodes/manipulation.cpp b/dwave/optimization/src/nodes/manipulation.cpp index 1c45621f..b4b2f063 100644 --- a/dwave/optimization/src/nodes/manipulation.cpp +++ b/dwave/optimization/src/nodes/manipulation.cpp @@ -1193,14 +1193,7 @@ void ResizeNode::initialize_state(State& state) const { const ssize_t size = this->size(); // the desired size of our state assert(size >= 0); // we're never dynamic - std::vector values; - values.reserve(size); - - // Fill in from our predecessor, up to our size. - // In c++23 we could use append_range(...) which would be nicer. - for (const auto& v : array_ptr_->view(state) | std::views::take(size)) { - values.emplace_back(v); - } + auto values = std::ranges::to(array_ptr_->view(state) | std::views::take(size)); // Now fill in everything else with our fill value assert( diff --git a/releasenotes/notes/c++23-60fb26a150a004e8.yaml b/releasenotes/notes/c++23-60fb26a150a004e8.yaml index a76532cb..01b4b7bd 100644 --- a/releasenotes/notes/c++23-60fb26a150a004e8.yaml +++ b/releasenotes/notes/c++23-60fb26a150a004e8.yaml @@ -1,5 +1,7 @@ --- features: - Compile C++ library with C++23. +upgrade: + - Drop support for GCC<14. other: - Build Windows wheels with LLVM rather than MSVC.