Skip to content

Implement state checkpointing for decision nodes - #587

Open
arcondello wants to merge 16 commits into
dwavesystems:mainfrom
arcondello:feature/decision-checkpoints
Open

Implement state checkpointing for decision nodes#587
arcondello wants to merge 16 commits into
dwavesystems:mainfrom
arcondello:feature/decision-checkpoints

Conversation

@arcondello

@arcondello arcondello commented Jul 17, 2026

Copy link
Copy Markdown
Member

Closes #510
Closes #552 by replacing it

AI Generation Disclosure

No AI was used to write the code. I intend to use it to review this PR.

@arcondello arcondello added the enhancement New feature or request label Jul 17, 2026
@arcondello
arcondello force-pushed the feature/decision-checkpoints branch from 6df7006 to 953304e Compare July 17, 2026 23:25
Comment thread dwave/optimization/src/nodes/collections.cpp Outdated
@arcondello
arcondello force-pushed the feature/decision-checkpoints branch 2 times, most recently from b4679c2 to 214c5e2 Compare July 20, 2026 18:59
This avoids using GCC11 which had some bugs in their ranges
implementation.
@arcondello
arcondello force-pushed the feature/decision-checkpoints branch from 86b97e8 to 9cd035b Compare July 20, 2026 19:18
Comment thread dwave/optimization/include/dwave-optimization/state.hpp Outdated
@arcondello
arcondello force-pushed the feature/decision-checkpoints branch 2 times, most recently from f1aa563 to 06b9dc5 Compare July 21, 2026 21:20
@arcondello
arcondello force-pushed the feature/decision-checkpoints branch from 06b9dc5 to f70699e Compare July 21, 2026 21:21
@arcondello
arcondello force-pushed the feature/decision-checkpoints branch from 30637e6 to 5fbe15b Compare July 27, 2026 18:46
@arcondello arcondello changed the title Implement state checkpointing for CollectionNode Implement state checkpointing for decision nodes Jul 27, 2026
Comment thread dwave/optimization/include/dwave-optimization/nodes/testing.hpp Outdated
@arcondello
arcondello force-pushed the feature/decision-checkpoints branch from 5fbe15b to 4402568 Compare July 27, 2026 18:51
@arcondello
arcondello marked this pull request as ready for review July 27, 2026 18:51
@arcondello
arcondello requested a review from fastbodin July 27, 2026 18:52
Comment thread dwave/optimization/src/graph.cpp
Comment thread tests/cpp/nodes/test_collections.cpp Outdated
Comment thread tests/cpp/nodes/test_collections.cpp Outdated
Comment thread tests/cpp/nodes/test_numbers.cpp Outdated
Comment thread tests/cpp/nodes/test_numbers.cpp
Comment thread tests/cpp/nodes/test_numbers.cpp Outdated
CHECK_THAT(inode_ptr->view(state), RangeEquals({8, 8, 8, 8, 8, 8, -3, 3}));
}
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit of a pain, but I think it might be worth adding a single test for BinaryNode with checkpointing to check that all the correct indices are cached with 1s and 0s (I am referring to the BinaryNode DisjointSparseSet).

THEN("Sum constraint sums, tracked indices, and state are correct") {
    CHECK(bnode_ptr->sum_constraints_lhs(state).size() == 1);
    CHECK(bnode_ptr->sum_constraints_lhs(state).data()[0].size() == 1);
    CHECK_THAT(bnode_ptr->sum_constraints_lhs(state)[0], RangeEquals({3}));
    check_indices<true>(state, bnode_ptr, 0, 0, {1, 2, 5}); // <- ***** This check is the important one ******
    check_indices<false>(state, bnode_ptr, 0, 0, {0, 3, 4}); // <- ***** And this one ******
    CHECK_THAT(bnode_ptr->view(state), RangeEquals(expected_init));
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that BinaryNode::assign_checkpoint() is very broken because it has a different update method. I think we need to refactor the various state data classes to use virtual methods.

Comment thread dwave/optimization/src/nodes/numbers.cpp
Comment thread dwave/optimization/src/nodes/numbers.cpp Outdated
Comment thread dwave/optimization/src/nodes/_state.hpp
Comment thread dwave/optimization/src/nodes/_state.hpp Outdated
Comment thread dwave/optimization/src/nodes/_checkpoints.cpp
Comment thread dwave/optimization/src/nodes/_checkpoints.cpp
Comment thread dwave/optimization/src/nodes/_checkpoints.hpp Outdated
Comment thread dwave/optimization/src/nodes/_checkpoints.cpp
Comment thread dwave/optimization/src/nodes/_checkpoints.cpp Outdated
Comment thread dwave/optimization/src/nodes/_checkpoints.cpp Outdated
@arcondello

Copy link
Copy Markdown
Member Author

I think I need to take another documentation pass. I'll try to do that today

@fastbodin fastbodin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Officially gone through everything. I need to look at the number.cpp and collections.cpp again. On the whole, looks good. Most of my comments above are clarifications. I will resolved them ASAP to avoid clogging the thread on this PR.

Comment thread dwave/optimization/src/nodes/collections.cpp
Comment thread dwave/optimization/src/nodes/collections.cpp
Comment thread dwave/optimization/src/nodes/collections.cpp Outdated
Comment thread tests/cpp/nodes/test_collections.cpp

auto* checkpoint_ptr = static_cast<NumberNodeCheckpoint_*>(checkpoint.get());

// todo: assert that this checkpoint is the latest

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing my understanding. You mean

        // Right now, you can only revert to the most recent checkpoint. It's
        // pretty straightforward to support going further back, but this is all
        // we need right now.
        assert(this->checkpoint_ptr<NumberNodeCheckpoint_>() == checkpoint_ptr);

Comment on lines +104 to +107
// The current "drop". The drop is used when a checkpoint is created while
// a node has some mutations already applied. This tells the checkpoint
// how to handle the diff associated with those mutations, i.e., the ones
// the checkpoint shouldn't be tracking.

@fastbodin fastbodin Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This doc string is good but omits some information. Possible alternative (that could be improved) based on my (possibly incorrect) understanding of drop.

// The current `drop` where `drop` is equal to the number of `Updates` 
// in the last `diff` cached by `updates_` that the checkpoint 
// is NOT responsible for tracking. Functionally, there are two cases. 
// 1. `drop == 0` and the checkpoint is responsible for all `Updates` 
//     stored in the last `diff` of `updates_`.
// 2. `drop > 0` and the checkpoint is *not* responsible for the last
//    `drop` number of `Updates` in the the last `diff` of `updates_`.

@fastbodin fastbodin Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized the above was in-fact, incorrect. Will try again.

const auto& [idx, old, _] :
state_data->diff() | std::views::reverse | std::views::take(excess_updates)
) {
state_data->set(idx, old);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this again, this is a bug. This does not take into account the running slice sums (should they be tracked). This was not caught in the tests because the relevant tests use exchange() which does not affect the running slice sums`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add checkpointing method(s) for decision array nodes

3 participants