diff --git a/dwave/optimization/src/nodes/collections.cpp b/dwave/optimization/src/nodes/collections.cpp index 2711c1f7..984044b0 100644 --- a/dwave/optimization/src/nodes/collections.cpp +++ b/dwave/optimization/src/nodes/collections.cpp @@ -835,7 +835,7 @@ ssize_t DisjointListNode::size_diff(const State& state) const { } void ListNode::initialize_state(State& state) const { - emplace_data_ptr(state, max_size_); + emplace_data_ptr(state, max_value_, min_size_); } void SetNode::initialize_state(State& state) const { diff --git a/releasenotes/notes/fix-list-node-max-size-smaller-than-n-5702a91cb4a6eedc.yaml b/releasenotes/notes/fix-list-node-max-size-smaller-than-n-5702a91cb4a6eedc.yaml new file mode 100644 index 00000000..5584058a --- /dev/null +++ b/releasenotes/notes/fix-list-node-max-size-smaller-than-n-5702a91cb4a6eedc.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fix an issue with ``ListVariable``s with a maximum size set to less than + ``n``, the size of the main set. In this case the underlying node's state + was not initialized correctly causing some valid operations to later fail. diff --git a/tests/cpp/nodes/test_collections.cpp b/tests/cpp/nodes/test_collections.cpp index 533fb55c..eebeeaa2 100644 --- a/tests/cpp/nodes/test_collections.cpp +++ b/tests/cpp/nodes/test_collections.cpp @@ -446,6 +446,68 @@ TEST_CASE("ListNode") { CHECK(sizeinfo.min == 0); CHECK(sizeinfo.max == 0); } + + GIVEN("A list(10, min_size=2, max_size=5)") { + auto l_ptr = graph.emplace_node(10, 2, 5); + + // Add a validation node + graph.emplace_node(l_ptr); + + WHEN("We default-initialize the state") { + auto state = graph.initialize_state(); + + THEN("The node's state defaults to range(2)") { + CHECK_THAT(l_ptr->view(state), RangeEquals({0, 1})); + CHECK(l_ptr->size(state) == 2); + CHECK_THAT(l_ptr->shape(state), RangeEquals({2})); + } + } + + WHEN("We initialize the state to the max size") { + auto state = graph.empty_state(); + l_ptr->initialize_state(state, {0, 1, 2, 3, 4}); + graph.initialize_state(state); + + THEN("The node's state is correct") { + CHECK_THAT(l_ptr->view(state), RangeEquals({0, 1, 2, 3, 4})); + CHECK(l_ptr->size(state) == 5); + CHECK_THAT(l_ptr->shape(state), RangeEquals({5})); + } + + AND_WHEN("We then mutate the node and propagate") { + l_ptr->exchange(state, 1, 3); // [0 3 2 1 4] + l_ptr->exchange(state, 1, 2); // [0 2 3 1 4] + l_ptr->shrink(state); // [0 2 3 1] + l_ptr->shrink(state); // [0 2 3] + l_ptr->exchange(state, 0, 7); // [7 2 3] + l_ptr->grow(state); // [7 2 3 1] + + graph.propagate(state, graph.descendants(state, {l_ptr})); + + THEN("The node's state reflects the relevant changes") { + CHECK_THAT(l_ptr->view(state), RangeEquals({7, 2, 3, 1})); + } + + AND_WHEN("We commit") { + graph.commit(state, graph.descendants(state, {l_ptr})); + + THEN("The changes persist") { + CHECK_THAT(l_ptr->view(state), RangeEquals({7, 2, 3, 1})); + CHECK(l_ptr->diff(state).size() == 0); + } + } + + AND_WHEN("We revert") { + graph.revert(state, graph.descendants(state, {l_ptr})); + + THEN("The changes are undone") { + CHECK_THAT(l_ptr->view(state), RangeEquals({0, 1, 2, 3, 4})); + CHECK(l_ptr->diff(state).size() == 0); + } + } + } + } + } } TEST_CASE("SetNode") {