From a75019505fa6182675cf2bc1ed0ab77804ea4b44 Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Fri, 10 Jul 2026 14:55:00 -0700 Subject: [PATCH 1/2] Rework dtypes with additional concepts and fixes --- .../include/dwave-optimization/iterators.hpp | 24 ++++--- .../include/dwave-optimization/typing.hpp | 68 ++++++++++++------- .../notes/dtype-fixes-a43f6a3f1872beb2.yaml | 10 +++ tests/cpp/meson.build | 1 + tests/cpp/test_typing.cpp | 33 +++++++++ 5 files changed, 103 insertions(+), 33 deletions(-) create mode 100644 releasenotes/notes/dtype-fixes-a43f6a3f1872beb2.yaml create mode 100644 tests/cpp/test_typing.cpp diff --git a/dwave/optimization/include/dwave-optimization/iterators.hpp b/dwave/optimization/include/dwave-optimization/iterators.hpp index a574174a..f547459f 100644 --- a/dwave/optimization/include/dwave-optimization/iterators.hpp +++ b/dwave/optimization/include/dwave-optimization/iterators.hpp @@ -20,7 +20,7 @@ #include #include -#include "dwave-optimization/common.hpp" +#include "dwave-optimization/common.hpp" // for ssize_t #include "dwave-optimization/typing.hpp" namespace dwave::optimization { @@ -38,11 +38,13 @@ concept shape_like = std::ranges::sized_range and std::integral -requires requires { +template +requires( + DType> and // const is OK + OptionalDType> and // const is OK // Cannot create an output iterator when From != To - requires std::is_const::value or std::is_same::value; -} class BufferIterator { + (std::is_const::value or std::same_as) +) class BufferIterator { public: // Iterator public types using difference_type = std::ptrdiff_t; @@ -83,7 +85,7 @@ requires requires { /// Note that the pointer to shape/strides is not owning! The lifespan of those /// pointers must exceed that of the iterator. BufferIterator(From* ptr, ssize_t ndim, const ssize_t* shape, const ssize_t* strides) noexcept - requires(DType) : + requires(DType>) : ptr_(ptr), format_(format_of()), ndim_(ndim), @@ -101,9 +103,10 @@ requires requires { } } } - template + template + requires(DType>) BufferIterator(T* ptr, ssize_t ndim, const ssize_t* shape, const ssize_t* strides) noexcept - requires(not DType) : + requires(not DType>) : ptr_(ptr), format_(format_of()), ndim_(ndim), @@ -124,7 +127,8 @@ requires requires { /// Convenience constructor when using shape/strides specified as spans (or things that can /// be read using a span). - template + template + requires(OptionalDType>) BufferIterator(T* ptr, std::span shape, std::span strides) : BufferIterator(ptr, shape.size(), shape.data(), strides.data()) { assert(std::ranges::size(shape) == std::ranges::size(strides)); @@ -434,7 +438,7 @@ requires requires { /// Return the current value at the pointer as a copy. value_type value_(From* ptr) const noexcept { - if constexpr (DType) { + if constexpr (DType>) { // In this case we know at compile-time what type we're converting from return *ptr; } else { diff --git a/dwave/optimization/include/dwave-optimization/typing.hpp b/dwave/optimization/include/dwave-optimization/typing.hpp index 5220f944..0707761a 100644 --- a/dwave/optimization/include/dwave-optimization/typing.hpp +++ b/dwave/optimization/include/dwave-optimization/typing.hpp @@ -16,28 +16,50 @@ #include #include +#include + +#include "dwave-optimization/type_list.hpp" namespace dwave::optimization { +// We want to support an enumerated list of data types, corresponding to a +// subset supported by NumPy. +// See https://numpy.org/doc/stable/reference/arrays.dtypes.html for more +// information. + +/// Supported dtypes as a type_list +using DTypes = type_list< + float, // np.float32 + double, // np.float64 + bool, // np.bool + std::int8_t, // np.int8 + std::int16_t, // np.int16 + std::int32_t, // np.int32 + std::int64_t>; // np.int64 + /// The `DType` concept is satisfied if and only if `T` is one of the -/// supported data types. This list is a subset of the `dtype`s supported by -/// NumPy and is designed for consistency/compatibility with NumPy. -/// See https://numpy.org/doc/stable/reference/arrays.dtypes.html for more -/// information. +/// supported dtypes. template -concept DType = std::same_as || // np.float32 - std::same_as || // np.float64 - std::same_as || // np.bool - std::same_as || // np.int8 - std::same_as || // np.int16 - std::same_as || // np.int32 - std::same_as; // np.int64 +concept DType = std::same_as or // np.float32 + std::same_as or // np.float64 + std::same_as or // np.bool + std::same_as or // np.int8 + std::same_as or // np.int16 + std::same_as or // np.int32 + std::same_as; // np.int64 +// We could have instead done DTypes::contains() but this way the compiler +// error message is a lot clearer /// The `OptionalDType` concept is satisfied if and only if `T` is `void` /// or satisfies `DType`. /// `void` is used to signal that the dtype is unknown at compile-time. template -concept OptionalDType = DType || std::same_as; +concept OptionalDType = DType or std::same_as; + +/// The `DTypeLike` concept is satisfied for supported types, as well as +/// const/volatile and references. +template +concept DTypeLike = DType>>; // Dev note: We'd like to work with `DType` only, but in order to work with the // buffer protocol (https://docs.python.org/3/c-api/buffer.html) we need to @@ -56,21 +78,21 @@ enum class FormatCharacter : char { signedchar_ = 'b', signedshort_ = 'h', signedlong_ = 'l', - signedlonglong_ = 'Q', + signedlonglong_ = 'q', }; /// Get the format character associated with a supported DType. -template +template constexpr FormatCharacter format_of() { - if constexpr (std::same_as) return FormatCharacter::float_; - if constexpr (std::same_as) return FormatCharacter::double_; - if constexpr (std::same_as) return FormatCharacter::bool_; - if constexpr (std::same_as) return FormatCharacter::int_; - if constexpr (std::same_as) return FormatCharacter::signedchar_; - if constexpr (std::same_as) return FormatCharacter::signedshort_; - if constexpr (std::same_as) return FormatCharacter::signedlong_; - if constexpr (std::same_as) - return FormatCharacter::signedlonglong_; + using U = std::remove_cv_t>; + if constexpr (std::same_as) return FormatCharacter::float_; + if constexpr (std::same_as) return FormatCharacter::double_; + if constexpr (std::same_as) return FormatCharacter::bool_; + if constexpr (std::same_as) return FormatCharacter::int_; + if constexpr (std::same_as) return FormatCharacter::signedchar_; + if constexpr (std::same_as) return FormatCharacter::signedshort_; + if constexpr (std::same_as) return FormatCharacter::signedlong_; + if constexpr (std::same_as) return FormatCharacter::signedlonglong_; } } // namespace dwave::optimization diff --git a/releasenotes/notes/dtype-fixes-a43f6a3f1872beb2.yaml b/releasenotes/notes/dtype-fixes-a43f6a3f1872beb2.yaml new file mode 100644 index 00000000..a7dbbda9 --- /dev/null +++ b/releasenotes/notes/dtype-fixes-a43f6a3f1872beb2.yaml @@ -0,0 +1,10 @@ +--- +features: + - Add ``DTypes`` ``type_list``. + - Add ``DTypeLike`` concept for references and ``const``-qualified types. +upgrade: + - The ``DType`` concept is no longer satisfied for ``const`` types. +fixes: + - | + Fix ``FormatCharacter::signedlonglong_`` to correctly use ``q`` rather than ``Q``. + See `#406 `_. diff --git a/tests/cpp/meson.build b/tests/cpp/meson.build index 58f3a6dd..3f1421fa 100644 --- a/tests/cpp/meson.build +++ b/tests/cpp/meson.build @@ -39,6 +39,7 @@ tests_all = executable( 'test_iterators.cpp', 'test_simplex.cpp', 'test_type_list.cpp', + 'test_typing.cpp', ], dependencies: [ catch2, diff --git a/tests/cpp/test_typing.cpp b/tests/cpp/test_typing.cpp new file mode 100644 index 00000000..f6c8937b --- /dev/null +++ b/tests/cpp/test_typing.cpp @@ -0,0 +1,33 @@ +// Copyright 2026 D-Wave +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include "dwave-optimization/typing.hpp" + +namespace dwave::optimization { + +void forwarding_reference_func(DTypeLike auto&& lhs) { } + +TEST_CASE("DTypeLike") { + int a = 1; + forwarding_reference_func(a); + forwarding_reference_func(static_cast(a)); + forwarding_reference_func(static_cast(a)); + forwarding_reference_func(std::move(a)); +} + +} // namespace dwave::optimization From dc512a1c2c1a0e9281919dd5c45b5bc2b388aef9 Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Fri, 10 Jul 2026 15:39:36 -0700 Subject: [PATCH 2/2] Use std::remove_cvref_t<> and don't use np.bool --- dwave/optimization/include/dwave-optimization/typing.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwave/optimization/include/dwave-optimization/typing.hpp b/dwave/optimization/include/dwave-optimization/typing.hpp index 0707761a..d042cb81 100644 --- a/dwave/optimization/include/dwave-optimization/typing.hpp +++ b/dwave/optimization/include/dwave-optimization/typing.hpp @@ -31,7 +31,7 @@ namespace dwave::optimization { using DTypes = type_list< float, // np.float32 double, // np.float64 - bool, // np.bool + bool, // np.bool_ std::int8_t, // np.int8 std::int16_t, // np.int16 std::int32_t, // np.int32 @@ -42,7 +42,7 @@ using DTypes = type_list< template concept DType = std::same_as or // np.float32 std::same_as or // np.float64 - std::same_as or // np.bool + std::same_as or // np.bool_ std::same_as or // np.int8 std::same_as or // np.int16 std::same_as or // np.int32 @@ -59,7 +59,7 @@ concept OptionalDType = DType or std::same_as; /// The `DTypeLike` concept is satisfied for supported types, as well as /// const/volatile and references. template -concept DTypeLike = DType>>; +concept DTypeLike = DType>; // Dev note: We'd like to work with `DType` only, but in order to work with the // buffer protocol (https://docs.python.org/3/c-api/buffer.html) we need to @@ -84,7 +84,7 @@ enum class FormatCharacter : char { /// Get the format character associated with a supported DType. template constexpr FormatCharacter format_of() { - using U = std::remove_cv_t>; + using U = std::remove_cvref_t; if constexpr (std::same_as) return FormatCharacter::float_; if constexpr (std::same_as) return FormatCharacter::double_; if constexpr (std::same_as) return FormatCharacter::bool_;