diff --git a/dwave/optimization/include/dwave-optimization/array.hpp b/dwave/optimization/include/dwave-optimization/array.hpp
index a4e6f7eb..7182b3af 100644
--- a/dwave/optimization/include/dwave-optimization/array.hpp
+++ b/dwave/optimization/include/dwave-optimization/array.hpp
@@ -654,22 +654,22 @@ class ScalarOutputMixin : public ScalarOutputMixin {
/// @copydoc Array::buff()
double const* buff(const State& state) const final {
- return this->template data_ptr(state)->buff();
+ return this->template data_ptr_(state)->buff();
}
/// @copydoc Node::commit()
void commit(State& state) const final {
- this->template data_ptr(state)->commit();
+ this->template data_ptr_(state)->commit();
}
/// @copydoc Array::diff()
std::span diff(const State& state) const final {
- return this->template data_ptr(state)->diff();
+ return this->template data_ptr_(state)->diff();
}
/// @copydoc Node::revert()
void revert(State& state) const final {
- this->template data_ptr(state)->revert();
+ this->template data_ptr_(state)->revert();
}
protected:
@@ -677,12 +677,12 @@ class ScalarOutputMixin : public ScalarOutputMixin {
/// Emplace a state with the given scalar value.
void emplace_state(State& state, double value) const {
- this->template emplace_data_ptr(state, value);
+ this->template emplace_data_ptr_(state, value);
}
/// Update the state value with the given value
void set_state(State& state, double value) const {
- this->template data_ptr(state)->set(value);
+ this->template data_ptr_(state)->set(value);
}
};
diff --git a/dwave/optimization/include/dwave-optimization/graph.hpp b/dwave/optimization/include/dwave-optimization/graph.hpp
index 7b2720b2..ad7681e5 100644
--- a/dwave/optimization/include/dwave-optimization/graph.hpp
+++ b/dwave/optimization/include/dwave-optimization/graph.hpp
@@ -55,123 +55,143 @@ class Graph {
Graph(Graph&&) noexcept = default;
Graph& operator=(Graph&&) noexcept = default;
+ /// Add a constraint node.
+ void add_constraint(ArrayNode* constraint_ptr);
+
+ /// Call commit on every `Node` in the `Graph`.
+ void commit(State& state) const;
+
+ /// Commit the changes on each changed node.
+ void commit(State& state, std::span changed) const;
+ void commit(State& state, std::vector&& changed) const;
+
+ /// Return the constraints of the graph as a span of nodes.
+ std::span constraints() noexcept { return constraints_; }
+ std::span constraints() const noexcept { return constraints_; }
+
+ /// Retrieve all of the decisions in the model
+ std::span decisions() noexcept { return decisions_; }
+ std::span decisions() const noexcept { return decisions_; }
+
+ /// Get the descendants of the source nodes, that is, all nodes that can be visited starting
+ /// from the sources.
+ static std::vector descendants(State& state, std::vector sources);
+ std::vector descendants(std::vector sources) const;
+
+ /// Add a new node to the graph.
template
NodeType* emplace_node(Args&&... args);
- State initialize_state() const;
- State initialize_state(); // topologically sorts first
- void initialize_state(State& state) const;
- void initialize_state(State& state); // topologically sorts first
+ /// Create a new "empty" state, that is a state with no nodes initialized.
+ /// The `const` version will fail if the model is not topologically sorted,
+ /// the non-`const` version will topologically sort the model.
State empty_state() const;
- State empty_state(); // topologically sorts first
+ State empty_state();
- // Initialize the state of the given node and all predecessors recursively.
- static void recursive_initialize(State& state, const Node* ptr);
- // Reset the state of the given node and all successors recursively.
- static void recursive_reset(State& state, const Node* ptr);
+ /// Calculate the current energy of the model, that is the output of the
+ /// objective node.
+ double energy(const State& state) const;
- // Sort the nodes topologically. This "locks" the model in that nodes cannot
- // be added to a topologically sorted model without invalidating the topological
- // ordering.
- void topological_sort();
+ /// Return `true` if the graph is feasible. A graph is feasible if all constraints
+ /// output `true`.
+ bool feasible(const State& state) const;
- // Reset the topological sort. Note that the decision variables will still
- // have a topological ordering.
- void reset_topological_sort();
+ /// Create a new state with all nodes initialized by their default initializer.
+ /// The `const` version will fail if the model is not topologically sorted,
+ /// the non-`const` version will topologically sort the model.
+ State initialize_state() const;
+ State initialize_state();
- // Whether or not the model is currently topologically sorted.
- // Models that are topologically sorted cannot be modified.
- bool topologically_sorted() const noexcept { return topologically_sorted_; }
+ /// Given a state, initialize any nodes that have not already been initialized.
+ /// The `const` version will fail if the model is not topologically sorted,
+ /// the non-`const` version will topologically sort the model.
+ void initialize_state(State& state) const;
+ void initialize_state(State& state);
+
+ /// Return a span over all of the input nodes in the model.
+ std::span inputs() noexcept { return inputs_; }
+ std::span inputs() const noexcept { return inputs_; }
+ /// All of the nodes in the graph.
std::span> nodes() const { return nodes_; }
- // Given the source (changing) nodes, update the model incrementally and accept the changes
- // according to the accept function.
- void propose(
- State& state,
- std::vector sources,
- std::function accept = [](const Graph&, State&) { return true; }
- ) const;
+ /// The number of decision nodes.
+ ssize_t num_decisions() const noexcept { return decisions_.size(); }
- // Get the descendants of the source nodes, that is, all nodes that can be visited starting from
- // the sources.
- static std::vector descendants(State& state, std::vector sources);
- std::vector descendants(std::vector sources) const;
+ /// The number of nodes in the model.
+ ssize_t num_nodes() const noexcept { return nodes_.size(); }
+
+ /// The number of constraints in the model.
+ ssize_t num_constraints() const noexcept { return constraints_.size(); }
+
+ /// The number of input nodes in the model.
+ ssize_t num_inputs() const noexcept { return inputs_.size(); }
+
+ /// Return a pointer to the node that is the objective of the model.
+ /// Will return nullptr if there is no objective set.
+ ArrayNode* objective() noexcept { return objective_ptr_; }
+ const ArrayNode* objective() const noexcept { return objective_ptr_; }
/// Call propagate on every `Node` in the `Graph`.
void propagate(State& state) const;
- // Call the propagate method on each node in changed. Note this does not call propagate on
- // the descendents of changed.
+ /// Call the propagate method on each node in changed. Note this does not call propagate on
+ /// the descendents of changed.
void propagate(State& state, std::span changed) const;
void propagate(State& state, std::vector&& changed) const;
- /// Call commit on every `Node` in the `Graph`.
- void commit(State& state) const;
+ /// Given the source (changing) nodes, update the model incrementally and accept the changes
+ /// according to the accept function.
+ void propose(
+ State& state,
+ std::vector sources,
+ std::function accept = [](const Graph&, State&) { return true; }
+ ) const;
- // Commit the changes on each changed node.
- void commit(State& state, std::span changed) const;
- void commit(State& state, std::vector&& changed) const;
+ /// Initialize the state of the given node and all predecessors recursively.
+ static void recursive_initialize(State& state, const Node* ptr);
+ /// Reset the state of the given node and all successors recursively.
+ static void recursive_reset(State& state, const Node* ptr);
+
+ /// Remove unused nodes from the graph.
+ ///
+ /// This method will reset the topological sort if there is one.
+ ///
+ /// A node is considered unused if all of the following are true:
+ /// * It is not a decision.
+ /// * It is not an ancestor of the objective.
+ /// * It is not an ancestor of a constraint.
+ /// * It has no "listeners" on its expired_ptr. Set ``ignore_listeners`` to
+ /// ``true`` to disable this condition.
+ ///
+ /// Returns the number of nodes removed from the graph.
+ ssize_t remove_unused_nodes(bool ignore_listeners = false);
/// Call revert on every `Node` in the `Graph`.
void revert(State& state) const;
- // Revert the changes on each changed node.
+ /// Revert the changes on each changed node.
void revert(State& state, std::span changed) const;
void revert(State& state, std::vector&& changed) const;
- // The number of decision nodes.
- ssize_t num_decisions() const noexcept { return decisions_.size(); }
-
- // The number of nodes in the model.
- ssize_t num_nodes() const noexcept { return nodes_.size(); }
-
- // The number of constraints in the model.
- ssize_t num_constraints() const noexcept { return constraints_.size(); }
-
- // The number of input nodes in the model.
- ssize_t num_inputs() const noexcept { return inputs_.size(); }
+ /// Reset the topological sort. Note that the decision variables will still
+ /// have a topological ordering.
+ void reset_topological_sort();
- // Specify the objective node. Must be an array with a single element.
- // To unset the objective provide nullptr.
+ /// Specify the objective node. Must be an array with a single element.
+ /// To unset the objective provide nullptr.
void set_objective(ArrayNode* objective_ptr);
- // Will return nullptr if there is no objective set.
- ArrayNode* objective() noexcept { return objective_ptr_; }
- const ArrayNode* objective() const noexcept { return objective_ptr_; }
-
- // Add a constraint node.
- void add_constraint(ArrayNode* constraint_ptr);
- std::span constraints() noexcept { return constraints_; }
- std::span constraints() const noexcept { return constraints_; }
-
- double energy(const State& state) const;
- bool feasible(const State& state) const;
-
- // Retrieve all of the decisions in the model
- std::span decisions() noexcept { return decisions_; }
- std::span decisions() const noexcept { return decisions_; }
-
- std::span inputs() noexcept { return inputs_; }
- std::span inputs() const noexcept { return inputs_; }
+ /// Sort the nodes topologically. This "locks" the model in that nodes cannot
+ /// be added to a topologically sorted model without invalidating the topological
+ /// ordering.
+ void topological_sort();
- // Remove unused nodes from the graph.
- //
- // This method will reset the topological sort if there is one.
- //
- // A node is considered unused if all of the following are true:
- // * It is not a decision.
- // * It is not an ancestor of the objective.
- // * It is not an ancestor of a constraint.
- // * It has no "listeners" on its expired_ptr. Set ``ignore_listeners`` to
- // ``true`` to disable this condition.
- //
- // Returns the number of nodes removed from the graph.
- ssize_t remove_unused_nodes(bool ignore_listeners = false);
+ /// Whether or not the model is currently topologically sorted.
+ /// Models that are topologically sorted cannot be modified.
+ bool topologically_sorted() const noexcept { return topologically_sorted_; }
private:
- static void visit_(Node* n_ptr, int* count_ptr);
-
std::vector> nodes_;
// The nodes with important semantic meanings to the model.
@@ -188,6 +208,16 @@ class Graph {
class Node {
public:
struct SuccessorView {
+ // Cannot construct a SuccessorView without a Node/index
+ SuccessorView() = delete;
+ ~SuccessorView() = default;
+
+ // Move/copy work normally
+ SuccessorView(const SuccessorView&) = default;
+ SuccessorView(SuccessorView&&) = default;
+ SuccessorView& operator=(const SuccessorView&) = default;
+ SuccessorView& operator=(SuccessorView&&) = default;
+
SuccessorView(Node* ptr, int index) noexcept : ptr(ptr), index(index) {}
// Can dereference just like a pointer
@@ -208,44 +238,71 @@ class Node {
Node() noexcept : expired_ptr_(new bool(false)) {}
virtual ~Node() { *expired_ptr_ = true; }
- const std::vector& predecessors() const { return predecessors_; }
- const std::vector& successors() const { return successors_; }
+ // Nodes cannot be moved or copied.
+ Node(const Node&) = delete;
+ Node(Node&&) noexcept = delete;
+ Node& operator=(const Node&) = delete;
+ Node& operator=(Node&&) noexcept = delete;
- virtual void initialize_state(State& state) const;
+ /// Methods for interrogating nodes as strings. Useful for error messages
+ /// and debugging. We roughly follow Python's scheme of repr() and str()
+ /// printing different information.
+ virtual std::string classname() const;
+
+ /// Commit any changing updates to the node.
+ virtual void commit(State& state) const = 0;
/// Return true if the node's state is deterministic - that is it's uniquely
/// derived from its predecessors. Defaults to `true`, except for decisions.
virtual bool deterministic_state() const { return true; }
- // The current topological index. Will be negative if unsorted.
- ssize_t topological_index() const { return topological_index_; }
+ /// Return a shared pointer to a bool value. When the node is destructed
+ /// the bool will be set to True
+ std::shared_ptr expired_ptr() const { return expired_ptr_; }
- // Incorporate any update(s) from predecessor nodes and then call the update()
- // method of any successors.
- // Note that calling update() on successors must happen *AFTER* the state
- // has been updated.
- // By default, this method calls the update() method of all succesors.
- // Subclasses will in general want to update their state and/or filter
- // the list of successors that need to be updated.
+ /// Initialize the state of the node.
+ virtual void initialize_state(State& state) const;
+
+ /// Return predecessors of the node.
+ const std::vector& predecessors() const { return predecessors_; }
+
+ /// Incorporate any update(s) from predecessor nodes and then call the update()
+ /// method of any successors.
+ /// Note that calling update() on successors must happen *AFTER* the state
+ /// has been updated.
+ /// By default, this method calls the update() method of all succesors.
+ /// Subclasses will in general want to update their state and/or filter
+ /// the list of successors that need to be updated.
virtual void propagate(State& state) const {
for (const auto& sv : successors()) {
sv->update(state, sv.index);
}
}
- virtual void commit(State& state) const = 0;
+ /// @copydoc Node::classname()
+ virtual std::string repr() const;
+
+ /// Revert any pending changes to the node.
virtual void revert(State& state) const = 0;
- // Called by predecessor nodes to signal that they have changes that need
- // to be incorporated into this node's state.
- // By default this method does nothing.
- // Subclasses will in general want to track which predececessors have been
- // updated and may even want to eagerly incorporate changes.
+ /// @copydoc Node::classname()
+ virtual std::string str() const;
+
+ /// Return the successors of the node.
+ const std::vector& successors() const { return successors_; }
+
+ /// The current topological index. Will be negative if unsorted.
+ ssize_t topological_index() const { return topological_index_; }
+
+ /// Called by predecessor nodes to signal that they have changes that need
+ /// to be incorporated into this node's state.
+ /// By default this method does nothing.
+ /// Subclasses will in general want to track which predececessors have been
+ /// updated and may even want to eagerly incorporate changes.
virtual void update(State& state, int index) const {}
- // Return a shared pointer to a bool value. When the node is destructed
- // the bool will be set to True
- std::shared_ptr expired_ptr() const { return expired_ptr_; }
+ /// Nodes are printable
+ friend std::ostream& operator<<(std::ostream& os, const Node& node);
friend void Graph::topological_sort();
friend void Graph::reset_topological_sort();
@@ -253,15 +310,6 @@ class Node {
friend NodeType* Graph::emplace_node(Args&&... args);
friend ssize_t Graph::remove_unused_nodes(bool ignore_listeners);
- // Methods for interrogating nodes as strings. Useful for error messages
- // and debugging. We roughly follow Python's scheme of repr() and str()
- // printing different information.
- virtual std::string classname() const;
- virtual std::string repr() const;
- virtual std::string str() const;
-
- friend std::ostream& operator<<(std::ostream& os, const Node& node);
-
protected:
// For use by non-dynamic node constructors.
Node(std::initializer_list nodes) noexcept :
@@ -274,7 +322,7 @@ class Node {
}
/// Add a predecessor node. Adds itself to the predecessor as a successor.
- void add_predecessor(Node* predecessor) {
+ void add_predecessor_(Node* predecessor) {
assert(
this->topological_index_ <= 0 &&
"cannot add a predecessor to a topologically sorted node"
@@ -283,50 +331,43 @@ class Node {
this->predecessors_.emplace_back(predecessor);
}
- /// Add a successor node. Adds itself to the successor as a predecessor.
- void add_successor(Node* successor) {
- assert(successor->topological_index_ <= 0 && "cannot add a topologically sorted successor");
- this->successors_.emplace_back(successor, successor->predecessors_.size());
- successor->predecessors_.emplace_back(this);
- }
-
template StateData>
- StateData* data_ptr(State& state) const {
+ StateData* data_ptr_(State& state) const {
const ssize_t index = topological_index();
- assert(index >= 0 && "must be topologically sorted");
- assert(state.size() > static_cast(index) && "unexpected state length");
- assert(state[index] != nullptr && "uninitialized state");
+ assert(index >= 0 and "must be topologically sorted");
+ assert(state.size() > static_cast(index) and "unexpected state length");
+ assert(state[index] != nullptr and "uninitialized state");
return static_cast(state[index].get());
}
template StateData>
- const StateData* data_ptr(const State& state) const {
+ const StateData* data_ptr_(const State& state) const {
const ssize_t index = topological_index();
- assert(index >= 0 && "must be topologically sorted");
- assert(state.size() > static_cast(index) && "unexpected state length");
- assert(state[index] != nullptr && "uninitialized state");
+ assert(index >= 0 and "must be topologically sorted");
+ assert(state.size() > static_cast(index) and "unexpected state length");
+ assert(state[index] != nullptr and "uninitialized state");
return static_cast(state[index].get());
}
template StateData, class... Args>
- void emplace_data_ptr(State& state, Args&&... args) const {
+ void emplace_data_ptr_(State& state, Args&&... args) const {
const ssize_t index = topological_index();
- assert(index >= 0 && "must be topologically sorted");
- assert(state.size() > static_cast(index) && "unexpected state length");
- assert(state[index] == nullptr && "already initialized state");
+ assert(index >= 0 and "must be topologically sorted");
+ assert(state.size() > static_cast(index) and "unexpected state length");
+ assert(state[index] == nullptr and "already initialized state");
state[index] = std::make_unique(std::forward(args)...);
}
// Whether this node type is elegible to be removed from the model
- virtual bool removable() const { return true; }
+ virtual bool removable_() const { return true; }
private:
ssize_t topological_index_ = -1; // negative is unset
std::vector predecessors_;
- std::vector successors_; // todo: successor view
+ std::vector successors_;
// Used to signal whether the node is expired to observers. Should be set
// to true when the Node is deallocated.
@@ -368,13 +409,13 @@ class DecisionNode : public Decision, public virtual Node {
/// Decision nodes by definition do not have a deterministic state.
bool deterministic_state() const final { return false; }
- // Decisions don't have predecessors so no one should be calling update().
- // Always throws a logic_error.
+ /// Decisions don't have predecessors so no one should be calling update().
+ /// Always throws a logic_error.
[[noreturn]] void update(State& state, int index) const override;
protected:
- // In general we do not allow decisions to be removed from models.
- bool removable() const override { return false; }
+ /// In general we do not allow decisions to be removed from models.
+ bool removable_() const override { return false; }
};
} // namespace dwave::optimization
diff --git a/dwave/optimization/include/dwave-optimization/nodes/collections.hpp b/dwave/optimization/include/dwave-optimization/nodes/collections.hpp
index 5b28d011..48b20119 100644
--- a/dwave/optimization/include/dwave-optimization/nodes/collections.hpp
+++ b/dwave/optimization/include/dwave-optimization/nodes/collections.hpp
@@ -155,7 +155,7 @@ class DisjointBitSetNode : public ArrayOutputMixin {
protected:
// This node is a pseudo-decision, so it is not removeable
- bool removable() const override { return false; }
+ bool removable_() const override { return false; }
const DisjointBitSetsNode* disjoint_bit_sets_node_;
const ssize_t set_index_;
@@ -256,7 +256,7 @@ class DisjointListNode : public ArrayOutputMixin {
protected:
// This node is a pseudo-decision, so it is not removeable
- bool removable() const override { return false; }
+ bool removable_() const override { return false; }
const DisjointListsNode* disjoint_list_node_ptr_;
const ssize_t list_index_;
diff --git a/dwave/optimization/src/graph.cpp b/dwave/optimization/src/graph.cpp
index 446cff65..01a6b0bb 100644
--- a/dwave/optimization/src/graph.cpp
+++ b/dwave/optimization/src/graph.cpp
@@ -21,7 +21,7 @@
#include
#include
-#if defined(__has_include) && __has_include()
+#if defined(__has_include) and __has_include()
#define _HAS_CXXABI
#include
#endif
@@ -30,164 +30,8 @@
namespace dwave::optimization {
-// Graph **********************************************************************
-
-void Graph::topological_sort() {
- if (topologically_sorted_) return;
-
- // The decisions already have their topological index assigned
-
- // Find a topological ordering of all of the non-decisions
- // The extra `visit` parameter is to allow for recursive lambdas
- // We iterate over successors in reverse order to preserve their original ordering
- // after sorting based on the topological index (which is counting down during DFS).
- auto visit = [](Node* n_ptr, int* count_ptr, auto&& visit) -> void {
- if (n_ptr->topological_index_ >= 0) return;
- if (n_ptr->topological_index_ == -2) throw std::logic_error("has cycles");
-
- // decisions should already be sorted
- assert(dynamic_cast(n_ptr) == nullptr && "unsorted decisions node");
-
- n_ptr->topological_index_ = -2;
-
- for (Node* m_ptr : n_ptr->successors() | std::views::reverse) {
- visit(m_ptr, count_ptr, visit);
- }
-
- n_ptr->topological_index_ = *count_ptr;
- *count_ptr -= 1;
- };
-
- int count = num_nodes() - 1;
- for (const std::unique_ptr& node_ptr : nodes_ | std::views::reverse) {
- // todo: iterative version
- visit(node_ptr.get(), &count, visit);
- }
-
- // Check that we have no gaps in our range.
- // Because the decisions were already sorted, that should determine the
- // count after assigning all others
- assert(count == num_decisions() - 1);
-
- // for later convenience, we sort the nodes_ by index
- // This moves all of the decisions to the front
- std::sort(
- nodes_.begin(),
- nodes_.end(),
- [](const std::unique_ptr& n_ptr, const std::unique_ptr& m_ptr) {
- return n_ptr->topological_index_ < m_ptr->topological_index_;
- }
- );
-
- topologically_sorted_ = true;
-}
-
-void Graph::reset_topological_sort() {
- if (!topologically_sorted_) return; // already unsorted
-
- // The decisions must have a consistent topological index, so we don't reset them
- std::for_each(
- // std::execution::par_unseq, // todo: performance testing. Probably won't help
- nodes_.begin() + num_decisions(),
- nodes_.end(),
- [](const std::unique_ptr& n_ptr) {
- assert(dynamic_cast(n_ptr.get()) == nullptr); // not a decision
- n_ptr->topological_index_ = -1;
- }
- );
-
- topologically_sorted_ = false;
-}
-
-State Graph::initialize_state() const {
- auto state = empty_state();
- initialize_state(state);
- return state;
-}
-
-State Graph::initialize_state() {
- topological_sort();
- return static_cast(this)->initialize_state();
-}
-
-void Graph::initialize_state(State& state) const {
- assert(static_cast(state.size()) == num_nodes() && "unexpected state length");
- assert(topologically_sorted_ && "graph must be topologically sorted");
-
- for (int i = 0, end = num_nodes(); i < end; ++i) {
- if (state[i]) continue; // should this clear any pending changes?
-
- nodes_[i]->initialize_state(state);
- }
-}
-
-void Graph::recursive_initialize(State& state, const Node* ptr) {
- ssize_t index = ptr->topological_index();
-
- if (index < 0) {
- throw std::logic_error("cannot initialize a node that has not been topologically sorted");
- }
-
- // make sure that the state is large enough
- if (index >= static_cast(state.size())) {
- state.resize(index + 1);
- }
-
- if (state[index]) return; // it's been created already
-
- // otherwise, make sure all predecessors are initialized
- for (const dwave::optimization::Node* pred_ptr : ptr->predecessors()) {
- recursive_initialize(state, pred_ptr);
- }
-
- ptr->initialize_state(state);
-}
-
-void Graph::recursive_reset(State& state, const Node* ptr) {
- ssize_t index = ptr->topological_index();
-
- if (index < 0) {
- throw std::logic_error("cannot reset a node that has not been topologically sorted");
- }
-
- // In the case that we're past the end of the state, we're by definition reset!
- if (index >= static_cast(state.size())) {
- return;
- }
-
- // We've already been reset so nothing to do
- if (!state[index]) return;
-
- // Otherwise, reset our own state and then all of our successors
- state[index].reset();
-
- for (const dwave::optimization::Node* successor_ptr : ptr->successors()) {
- if (successor_ptr->topological_index() < 0) continue; // nothing to reset
- recursive_reset(state, successor_ptr);
- }
-}
-
-void Graph::initialize_state(State& state) {
- topological_sort();
- static_cast(this)->initialize_state(state);
-}
-
-State Graph::empty_state() const { return State(num_nodes()); }
-State Graph::empty_state() {
- topological_sort();
- return static_cast(this)->empty_state();
-}
-
-void Graph::set_objective(ArrayNode* objective_ptr) {
- // nullptr is an unset objective, so we allow it.
- if (objective_ptr != nullptr && objective_ptr->size() != 1) {
- throw std::invalid_argument("objective must have a single output");
- }
- this->objective_ptr_ = objective_ptr;
-}
-
void Graph::add_constraint(ArrayNode* constraint_ptr) {
- if (!constraint_ptr->logical()) {
+ if (not constraint_ptr->logical()) {
throw std::invalid_argument("constraint must have a logical output");
}
// todo: we could substitute an AND on the user's behalf, or we could allow
@@ -201,16 +45,29 @@ void Graph::add_constraint(ArrayNode* constraint_ptr) {
constraints_.emplace_back(constraint_ptr);
}
-double Graph::energy(const State& state) const {
- if (objective_ptr_ == nullptr) return 0;
- return objective_ptr_->view(state).front();
+void Graph::commit(State& state) const {
+ std::ranges::for_each(nodes(), [&state](const auto& ptr) { ptr->commit(state); });
}
-bool Graph::feasible(const State& state) const {
- for (const Array* ptr : constraints_) {
- assert(ptr->size(state) == 1);
- if (!(ptr->view(state)[0])) return false;
- }
- return true;
+
+void Graph::commit(State& state, std::span changed) const {
+ std::for_each(
+ // std::execution::par_unseq, // todo: test performance. Might help!
+ changed.begin(),
+ changed.end(),
+ [&state](const Node* n_ptr) { n_ptr->commit(state); }
+ );
+
+ assert(([&state, &changed]() -> bool {
+ for (const Node* n_ptr : changed) {
+ if (dynamic_cast(n_ptr) == nullptr) continue;
+ if (not dynamic_cast(n_ptr)->diff(state).empty()) return false;
+ }
+ return true;
+ })());
+}
+
+void Graph::commit(State& state, std::vector&& changed) const {
+ commit(state, std::span(changed));
}
// Performs Breadth First Search and sort the nodes visited according to their topological number.
@@ -251,72 +108,68 @@ std::vector Graph::descendants(std::vector sources) co
return descendants(state, sources);
}
-void Graph::propagate(State& state) const {
- std::ranges::for_each(nodes(), [&state](const auto& ptr) { ptr->propagate(state); });
-}
+State Graph::empty_state() const { return State(num_nodes()); }
-void Graph::propagate(State& state, std::span queue_to_update) const {
- for (const Node* node_ptr : queue_to_update) {
- node_ptr->propagate(state);
+State Graph::empty_state() {
+ topological_sort();
+ return static_cast(this)->empty_state();
+}
- // Note: commented because we split the methods.
- // We might incur in a small performance degradation.
- // state[node_ptr->topological_index()]->mark = false;
+double Graph::energy(const State& state) const {
+ if (objective_ptr_ == nullptr) return 0;
+ return objective_ptr_->view(state).front();
+}
+bool Graph::feasible(const State& state) const {
+ for (const Array* ptr : constraints_) {
+ assert(ptr->size(state) == 1);
+ if (not(ptr->view(state)[0])) return false;
}
+ return true;
}
-void Graph::propagate(State& state, std::vector&& changed) const {
- return propagate(state, std::span(changed));
+State Graph::initialize_state() const {
+ auto state = empty_state();
+ initialize_state(state);
+ return state;
}
-void Graph::commit(State& state) const {
- std::ranges::for_each(nodes(), [&state](const auto& ptr) { ptr->commit(state); });
+State Graph::initialize_state() {
+ topological_sort();
+ return static_cast(this)->initialize_state();
}
-void Graph::commit(State& state, std::span changed) const {
- std::for_each(
- // std::execution::par_unseq, // todo: test performance. Might help!
- changed.begin(),
- changed.end(),
- [&state](const Node* n_ptr) { n_ptr->commit(state); }
- );
+void Graph::initialize_state(State& state) const {
+ assert(static_cast(state.size()) == num_nodes() and "unexpected state length");
+ assert(topologically_sorted_ and "graph must be topologically sorted");
- assert(([&state, &changed]() -> bool {
- for (const Node* n_ptr : changed) {
- if (dynamic_cast(n_ptr) == nullptr) continue;
- if (!dynamic_cast(n_ptr)->diff(state).empty()) return false;
- }
- return true;
- })());
+ for (int i = 0, end = num_nodes(); i < end; ++i) {
+ if (state[i]) continue; // should this clear any pending changes?
+
+ nodes_[i]->initialize_state(state);
+ }
}
-void Graph::commit(State& state, std::vector&& changed) const {
- commit(state, std::span(changed));
+void Graph::initialize_state(State& state) {
+ topological_sort();
+ static_cast(this)->initialize_state(state);
}
-void Graph::revert(State& state) const {
- std::ranges::for_each(nodes(), [&state](const auto& ptr) { ptr->revert(state); });
+void Graph::propagate(State& state) const {
+ std::ranges::for_each(nodes(), [&state](const auto& ptr) { ptr->propagate(state); });
}
-void Graph::revert(State& state, std::span changed) const {
- std::for_each(
- // std::execution::par_unseq, // todo: test performance. Might help!
- changed.begin(),
- changed.end(),
- [&state](const Node* n_ptr) { n_ptr->revert(state); }
- );
+void Graph::propagate(State& state, std::span queue_to_update) const {
+ for (const Node* node_ptr : queue_to_update) {
+ node_ptr->propagate(state);
- assert(([&state, &changed]() -> bool {
- for (const Node* n_ptr : changed) {
- if (dynamic_cast(n_ptr) == nullptr) continue;
- if (!dynamic_cast(n_ptr)->diff(state).empty()) return false;
- }
- return true;
- })());
+ // Note: commented because we split the methods.
+ // We might incur in a small performance degradation.
+ // state[node_ptr->topological_index()]->mark = false;
+ }
}
-void Graph::revert(State& state, std::vector&& changed) const {
- revert(state, std::span(changed));
+void Graph::propagate(State& state, std::vector&& changed) const {
+ return propagate(state, std::span(changed));
}
// Note: we pass the vector of changed nodes by value as we expect it to be rather small. Revisit if
@@ -340,6 +193,52 @@ void Graph::propose(
}
}
+void Graph::recursive_initialize(State& state, const Node* ptr) {
+ ssize_t index = ptr->topological_index();
+
+ if (index < 0) {
+ throw std::logic_error("cannot initialize a node that has not been topologically sorted");
+ }
+
+ // make sure that the state is large enough
+ if (index >= static_cast(state.size())) {
+ state.resize(index + 1);
+ }
+
+ if (state[index]) return; // it's been created already
+
+ // otherwise, make sure all predecessors are initialized
+ for (const dwave::optimization::Node* pred_ptr : ptr->predecessors()) {
+ recursive_initialize(state, pred_ptr);
+ }
+
+ ptr->initialize_state(state);
+}
+
+void Graph::recursive_reset(State& state, const Node* ptr) {
+ ssize_t index = ptr->topological_index();
+
+ if (index < 0) {
+ throw std::logic_error("cannot reset a node that has not been topologically sorted");
+ }
+
+ // In the case that we're past the end of the state, we're by definition reset!
+ if (index >= static_cast(state.size())) {
+ return;
+ }
+
+ // We've already been reset so nothing to do
+ if (not state[index]) return;
+
+ // Otherwise, reset our own state and then all of our successors
+ state[index].reset();
+
+ for (const dwave::optimization::Node* successor_ptr : ptr->successors()) {
+ if (successor_ptr->topological_index() < 0) continue; // nothing to reset
+ recursive_reset(state, successor_ptr);
+ }
+}
+
ssize_t Graph::remove_unused_nodes(bool ignore_listeners) {
// Establish a topological ordering. We'll use the fact that the node list
// is ordered, but we're going to mess with the topological indices!
@@ -364,13 +263,13 @@ ssize_t Graph::remove_unused_nodes(bool ignore_listeners) {
}
// as is the objective if it exists
- if (objective_ptr_ && objective_ptr_->topological_index_ >= num_decisions) {
+ if (objective_ptr_ and objective_ptr_->topological_index_ >= num_decisions) {
objective_ptr_->topological_index_ = keep;
}
// If we're not ignoring listeners, we check if anyone is "listening" to
// the expired_ptr_. If so, we keep the node.
- if (!ignore_listeners) {
+ if (not ignore_listeners) {
for (auto& ptr : nodes_ | std::views::drop(num_decisions)) {
if (ptr->expired_ptr_.use_count() > 1) ptr->topological_index_ = keep;
}
@@ -380,7 +279,7 @@ ssize_t Graph::remove_unused_nodes(bool ignore_listeners) {
// Now walk backwards through the topologically sorted node list
// removing any nodes with no successors that we haven't marked.
for (auto& ptr : nodes_ | std::views::drop(num_decisions) | std::views::reverse) {
- if (!ptr->removable()) continue; // some nodes can never be removed
+ if (not ptr->removable_()) continue; // some nodes can never be removed
if (ptr->topological_index_ == keep) continue; // we marked these to keep
if (ptr->successors().size() > 0) continue; // this node is used by other nodes
@@ -398,7 +297,7 @@ ssize_t Graph::remove_unused_nodes(bool ignore_listeners) {
}
// Traverse the nodes_ one last time removing any nullptrs we created
- std::erase_if(nodes_, [](const auto& ptr) { return !ptr; });
+ std::erase_if(nodes_, [](const auto& ptr) { return not ptr; });
// Undo all of the weird stuff we did with the topological indices
reset_topological_sort();
@@ -407,12 +306,110 @@ ssize_t Graph::remove_unused_nodes(bool ignore_listeners) {
return num_nodes_before - this->num_nodes();
}
-// Node ***********************************************************************
+void Graph::reset_topological_sort() {
+ if (not topologically_sorted_) return; // already unsorted
+
+ // The decisions must have a consistent topological index, so we don't reset them
+ std::for_each(
+ // std::execution::par_unseq, // todo: performance testing. Probably won't help
+ nodes_.begin() + num_decisions(),
+ nodes_.end(),
+ [](const std::unique_ptr& n_ptr) {
+ assert(dynamic_cast(n_ptr.get()) == nullptr); // not a decision
+ n_ptr->topological_index_ = -1;
+ }
+ );
+
+ topologically_sorted_ = false;
+}
+
+void Graph::revert(State& state) const {
+ std::ranges::for_each(nodes(), [&state](const auto& ptr) { ptr->revert(state); });
+}
+
+void Graph::revert(State& state, std::span changed) const {
+ std::for_each(
+ // std::execution::par_unseq, // todo: test performance. Might help!
+ changed.begin(),
+ changed.end(),
+ [&state](const Node* n_ptr) { n_ptr->revert(state); }
+ );
+
+ assert(([&state, &changed]() -> bool {
+ for (const Node* n_ptr : changed) {
+ if (dynamic_cast(n_ptr) == nullptr) continue;
+ if (not dynamic_cast(n_ptr)->diff(state).empty()) return false;
+ }
+ return true;
+ })());
+}
+
+void Graph::revert(State& state, std::vector&& changed) const {
+ revert(state, std::span(changed));
+}
+
+void Graph::set_objective(ArrayNode* objective_ptr) {
+ // nullptr is an unset objective, so we allow it.
+ if (objective_ptr != nullptr and objective_ptr->size() != 1) {
+ throw std::invalid_argument("objective must have a single output");
+ }
+ this->objective_ptr_ = objective_ptr;
+}
+
+void Graph::topological_sort() {
+ if (topologically_sorted_) return;
+
+ // The decisions already have their topological index assigned
+
+ // Find a topological ordering of all of the non-decisions
+ // The extra `visit` parameter is to allow for recursive lambdas
+ // We iterate over successors in reverse order to preserve their original ordering
+ // after sorting based on the topological index (which is counting down during DFS).
+ auto visit = [](Node* n_ptr, int* count_ptr, auto&& visit) -> void {
+ if (n_ptr->topological_index_ >= 0) return;
+ if (n_ptr->topological_index_ == -2) throw std::logic_error("has cycles");
+
+ // decisions should already be sorted
+ assert(dynamic_cast(n_ptr) == nullptr and "unsorted decisions node");
+
+ n_ptr->topological_index_ = -2;
+
+ for (Node* m_ptr : n_ptr->successors() | std::views::reverse) {
+ visit(m_ptr, count_ptr, visit);
+ }
+
+ n_ptr->topological_index_ = *count_ptr;
+ *count_ptr -= 1;
+ };
+
+ int count = num_nodes() - 1;
+ for (const std::unique_ptr& node_ptr : nodes_ | std::views::reverse) {
+ // todo: iterative version
+ visit(node_ptr.get(), &count, visit);
+ }
+
+ // Check that we have no gaps in our range.
+ // Because the decisions were already sorted, that should determine the
+ // count after assigning all others
+ assert(count == num_decisions() - 1);
+
+ // for later convenience, we sort the nodes_ by index
+ // This moves all of the decisions to the front
+ std::sort(
+ nodes_.begin(),
+ nodes_.end(),
+ [](const std::unique_ptr& n_ptr, const std::unique_ptr& m_ptr) {
+ return n_ptr->topological_index_ < m_ptr->topological_index_;
+ }
+ );
+
+ topologically_sorted_ = true;
+}
void Node::initialize_state(State& state) const {
- assert(topological_index_ >= 0 && "must be topologically sorted");
- assert(static_cast(state.size()) > topological_index_ && "unexpected state length");
- assert(state[topological_index_] == nullptr && "already initialized state");
+ assert(topological_index_ >= 0 and "must be topologically sorted");
+ assert(static_cast(state.size()) > topological_index_ and "unexpected state length");
+ assert(state[topological_index_] == nullptr and "already initialized state");
state[topological_index_] = std::make_unique();
}
@@ -473,8 +470,6 @@ std::string Node::str() const { return classname(); }
std::ostream& operator<<(std::ostream& os, const Node& node) { return os << node.repr(); }
-// DecisionNode ***************************************************************
-
[[noreturn]] void DecisionNode::update(State& state, int index) const {
throw std::logic_error("update() called on a decisison variable");
}
diff --git a/dwave/optimization/src/nodes/binaryop.cpp b/dwave/optimization/src/nodes/binaryop.cpp
index 70084906..2241cc92 100644
--- a/dwave/optimization/src/nodes/binaryop.cpp
+++ b/dwave/optimization/src/nodes/binaryop.cpp
@@ -194,23 +194,23 @@ BinaryOpNode::BinaryOpNode(ArrayNode* a_ptr, ArrayNode* b_ptr) :
calculate_integral(operands_[0], operands_[1])
),
sizeinfo_(binaryop_calculate_sizeinfo(this, operands_[0], operands_[1])) {
- this->add_predecessor(a_ptr);
- this->add_predecessor(b_ptr);
+ this->add_predecessor_(a_ptr);
+ this->add_predecessor_(b_ptr);
}
template
double const* BinaryOpNode::buff(const State& state) const {
- return data_ptr(state)->buff();
+ return data_ptr_(state)->buff();
}
template
std::span BinaryOpNode::diff(const State& state) const {
- return data_ptr(state)->diff();
+ return data_ptr_(state)->diff();
}
template
void BinaryOpNode::commit(State& state) const {
- data_ptr(state)->commit();
+ data_ptr_(state)->commit();
}
template
@@ -254,7 +254,7 @@ void BinaryOpNode::initialize_state(State& state) const {
unreachable();
}
- emplace_data_ptr(state, std::move(values));
+ emplace_data_ptr_(state, std::move(values));
}
template
@@ -274,7 +274,7 @@ double BinaryOpNode::min() const {
template
void BinaryOpNode::propagate(State& state) const {
- auto ptr = data_ptr(state);
+ auto ptr = data_ptr_(state);
const Array* lhs_ptr = operands_[0];
const Array* rhs_ptr = operands_[1];
@@ -389,7 +389,7 @@ void BinaryOpNode::propagate(State& state) const {
template
void BinaryOpNode::revert(State& state) const {
- data_ptr(state)->revert();
+ data_ptr_(state)->revert();
}
template
@@ -434,7 +434,7 @@ ssize_t BinaryOpNode::size(const State& state) const {
template
ssize_t BinaryOpNode::size_diff(const State& state) const {
- return data_ptr(state)->size_diff();
+ return data_ptr_(state)->size_diff();
}
SizeInfo binaryop_calculate_sizeinfo(
diff --git a/dwave/optimization/src/nodes/collections.cpp b/dwave/optimization/src/nodes/collections.cpp
index 1a38942e..0ea4f805 100644
--- a/dwave/optimization/src/nodes/collections.cpp
+++ b/dwave/optimization/src/nodes/collections.cpp
@@ -239,33 +239,35 @@ void CollectionNode::assign(State& state, std::vector values) const {
// Check that the values are a proper subset and fill out the invisible part
auto augemented = augment_collection_(std::move(values), max_value_);
- data_ptr(state)->assign(std::move(augemented), size);
+ data_ptr_(state)->assign(std::move(augemented), size);
}
-void CollectionNode::commit(State& state) const { data_ptr(state)->commit(); }
+void CollectionNode::commit(State& state) const {
+ data_ptr_(state)->commit();
+}
const double* CollectionNode::buff(const State& state) const {
- return data_ptr(state)->buff();
+ return data_ptr_(state)->buff();
}
std::span CollectionNode::diff(const State& state) const {
- return data_ptr(state)->diff();
+ return data_ptr_(state)->diff();
}
void CollectionNode::exchange(State& state, ssize_t i, ssize_t j) const {
if (i == j) return;
- data_ptr(state)->exchange(i, j); // handles the asserts
+ data_ptr_(state)->exchange(i, j); // handles the asserts
}
void CollectionNode::rotate(State& state, ssize_t dest_idx, ssize_t src_idx) const {
if (src_idx == dest_idx) return;
- data_ptr(state)->rotate(dest_idx, src_idx); // handles the asserts
+ data_ptr_(state)->rotate(dest_idx, src_idx); // handles the asserts
}
void CollectionNode::grow(State& state) const {
assert(this->dynamic());
- assert(data_ptr(state)->size() < max_size_);
- data_ptr(state)->grow();
+ assert(data_ptr_(state)->size() < max_size_);
+ data_ptr_(state)->grow();
}
void CollectionNode::initialize_state(State& state, std::vector values) const {
@@ -276,7 +278,7 @@ void CollectionNode::initialize_state(State& state, std::vector values)
// Check that the values are a proper subset and fill out the invisible part
auto augemented = augment_collection_(std::move(values), max_value_);
- emplace_data_ptr(state, std::move(augemented), size);
+ emplace_data_ptr_(state, std::move(augemented), size);
}
bool CollectionNode::integral() const { return true; }
@@ -288,28 +290,30 @@ double CollectionNode::max() const {
return max_value_ - 1;
}
-void CollectionNode::revert(State& state) const { data_ptr(state)->revert(); }
+void CollectionNode::revert(State& state) const {
+ data_ptr_(state)->revert();
+}
std::span CollectionNode::shape(const State& state) const {
if (not dynamic()) {
assert(min_size_ == max_size_);
return std::span(&min_size_, 1);
}
- return data_ptr(state)->shape();
+ return data_ptr_(state)->shape();
}
void CollectionNode::shrink(State& state) const {
assert(dynamic());
assert(size(state) > min_size_);
- data_ptr(state)->shrink();
+ data_ptr_(state)->shrink();
}
ssize_t CollectionNode::size(const State& state) const {
if (ssize_t size = this->size(); size >= 0) {
- assert(data_ptr(state)->size() == size);
+ assert(data_ptr_(state)->size() == size);
return size;
}
- return data_ptr(state)->size();
+ return data_ptr_(state)->size();
}
SizeInfo CollectionNode::sizeinfo() const {
@@ -319,10 +323,10 @@ SizeInfo CollectionNode::sizeinfo() const {
ssize_t CollectionNode::size_diff(const State& state) const {
if (not dynamic()) {
- assert(data_ptr(state)->size_diff() == 0);
+ assert(data_ptr_(state)->size_diff() == 0);
return 0;
}
- return data_ptr(state)->size_diff();
+ return data_ptr_(state)->size_diff();
}
struct DisjointBitSetsNodeData_ : NodeStateData {
@@ -429,24 +433,24 @@ DisjointBitSetsNode::DisjointBitSetsNode(ssize_t primary_set_size, ssize_t num_d
}
void DisjointBitSetsNode::initialize_state(State& state) const {
- emplace_data_ptr(state, primary_set_size_, num_disjoint_sets_);
+ emplace_data_ptr_(state, primary_set_size_, num_disjoint_sets_);
}
void DisjointBitSetsNode::initialize_state(
State& state,
const std::vector>& contents
) const {
- emplace_data_ptr(
+ emplace_data_ptr_(
state, primary_set_size_, num_disjoint_sets_, contents
);
}
void DisjointBitSetsNode::commit(State& state) const {
- data_ptr(state)->commit();
+ data_ptr_(state)->commit();
}
void DisjointBitSetsNode::revert(State& state) const {
- data_ptr(state)->revert();
+ data_ptr_(state)->revert();
}
void DisjointBitSetsNode::swap_between_sets(
@@ -455,13 +459,13 @@ void DisjointBitSetsNode::swap_between_sets(
ssize_t to_disjoint_set,
ssize_t element
) const {
- data_ptr(state)->swap_between_sets(
+ data_ptr_(state)->swap_between_sets(
from_disjoint_set, to_disjoint_set, element
);
}
ssize_t DisjointBitSetsNode::get_containing_set_index(State& state, ssize_t element) const {
- return data_ptr(state)->get_containing_set_index(element);
+ return data_ptr_(state)->get_containing_set_index(element);
}
DisjointBitSetNode::DisjointBitSetNode(DisjointBitSetsNode* disjoint_bit_sets_node) :
@@ -472,7 +476,7 @@ DisjointBitSetNode::DisjointBitSetNode(DisjointBitSetsNode* disjoint_bit_sets_no
if (set_index_ >= disjoint_bit_sets_node_->num_disjoint_sets()) {
throw std::length_error("disjoint-bit-set node already has all output nodes");
}
- add_predecessor(disjoint_bit_sets_node);
+ add_predecessor_(disjoint_bit_sets_node);
}
const double* DisjointBitSetNode::buff(const State& state) const {
@@ -726,7 +730,7 @@ DisjointListsNode::DisjointListsNode(ssize_t primary_set_size, ssize_t num_disjo
}
void DisjointListsNode::initialize_state(State& state) const {
- emplace_data_ptr(
+ emplace_data_ptr_(
state, this->primary_set_size(), this->num_disjoint_lists()
);
}
@@ -735,22 +739,22 @@ void DisjointListsNode::initialize_state(
State& state,
std::vector> contents
) const {
- emplace_data_ptr(
+ emplace_data_ptr_(
state, this->primary_set_size(), this->num_disjoint_lists(), std::move(contents)
);
}
void DisjointListsNode::commit(State& state) const {
- data_ptr(state)->commit();
+ data_ptr_(state)->commit();
}
void DisjointListsNode::revert(State& state) const {
- data_ptr(state)->revert();
+ data_ptr_(state)->revert();
}
void DisjointListsNode::propagate(State& state) const {
#ifndef NDEBUG
- auto data = data_ptr(state);
+ auto data = data_ptr_(state);
std::vector items(this->primary_set_size());
for (auto const& list : data->lists) {
for (const auto& item : list) {
@@ -770,7 +774,7 @@ void DisjointListsNode::propagate(State& state) const {
}
ssize_t DisjointListsNode::get_disjoint_list_size(State& state, ssize_t list_index) const {
- auto data = data_ptr(state);
+ auto data = data_ptr_(state);
auto size = data->lists[list_index].size();
return size;
}
@@ -782,7 +786,7 @@ void DisjointListsNode::rotate_in_list(
ssize_t dest_idx
) const {
if (src_idx == dest_idx) return;
- data_ptr(state)->rotate_in_list(list_index, src_idx, dest_idx);
+ data_ptr_(state)->rotate_in_list(list_index, src_idx, dest_idx);
}
void DisjointListsNode::set_state(
@@ -790,7 +794,7 @@ void DisjointListsNode::set_state(
ssize_t list_index,
const std::span& new_values
) const {
- data_ptr(state)->set_state(list_index, new_values);
+ data_ptr_(state)->set_state(list_index, new_values);
}
void DisjointListsNode::swap_in_list(
@@ -801,7 +805,7 @@ void DisjointListsNode::swap_in_list(
) const {
if (element_i == element_j) return;
- data_ptr(state)->swap_in_list(list_index, element_i, element_j);
+ data_ptr_(state)->swap_in_list(list_index, element_i, element_j);
}
void DisjointListsNode::pop_to_list(
@@ -811,7 +815,7 @@ void DisjointListsNode::pop_to_list(
ssize_t to_list_index,
ssize_t element_j
) const {
- data_ptr(state)->pop_to_list(
+ data_ptr_(state)->pop_to_list(
from_list_index, element_i, to_list_index, element_j
);
}
@@ -825,7 +829,7 @@ DisjointListNode::DisjointListNode(DisjointListsNode* disjoint_list_node) :
throw std::length_error("disjoint-list node already has all output nodes");
}
- add_predecessor(disjoint_list_node);
+ add_predecessor_(disjoint_list_node);
}
const double* DisjointListNode::buff(const State& state) const {
@@ -871,11 +875,11 @@ ssize_t DisjointListNode::size_diff(const State& state) const {
}
void ListNode::initialize_state(State& state) const {
- emplace_data_ptr(state, max_value_, min_size_);
+ emplace_data_ptr_(state, max_value_, min_size_);
}
void SetNode::initialize_state(State& state) const {
- emplace_data_ptr(state, max_value_, min_size_);
+ emplace_data_ptr_(state, max_value_, min_size_);
}
} // namespace dwave::optimization
diff --git a/dwave/optimization/src/nodes/creation.cpp b/dwave/optimization/src/nodes/creation.cpp
index c6976204..31860722 100644
--- a/dwave/optimization/src/nodes/creation.cpp
+++ b/dwave/optimization/src/nodes/creation.cpp
@@ -282,7 +282,7 @@ ARangeNode::ARangeNode(ssize_t start, ssize_t stop, ArrayNode* step) :
step_(step),
values_minmax_(calculate_values_minmax(start, stop, step)),
sizeinfo_(calculate_arange_sizeinfo(this, start, stop, step)) {
- add_predecessor(step);
+ add_predecessor_(step);
}
ARangeNode::ARangeNode(ssize_t start, ArrayNode* stop, ssize_t step) :
ArrayOutputMixin(range_shape(start, stop, step)),
@@ -291,7 +291,7 @@ ARangeNode::ARangeNode(ssize_t start, ArrayNode* stop, ssize_t step) :
step_(step),
values_minmax_(calculate_values_minmax(start, stop, step)),
sizeinfo_(calculate_arange_sizeinfo(this, start, stop, step)) {
- add_predecessor(stop);
+ add_predecessor_(stop);
}
ARangeNode::ARangeNode(ssize_t start, ArrayNode* stop, ArrayNode* step) :
ArrayOutputMixin(range_shape(start, stop, step)),
@@ -300,8 +300,8 @@ ARangeNode::ARangeNode(ssize_t start, ArrayNode* stop, ArrayNode* step) :
step_(step),
values_minmax_(calculate_values_minmax(start, stop, step)),
sizeinfo_(calculate_arange_sizeinfo(this, start, stop, step)) {
- add_predecessor(stop);
- add_predecessor(step);
+ add_predecessor_(stop);
+ add_predecessor_(step);
}
ARangeNode::ARangeNode(ArrayNode* start, ssize_t stop, ssize_t step) :
ArrayOutputMixin(range_shape(start, stop, step)),
@@ -310,7 +310,7 @@ ARangeNode::ARangeNode(ArrayNode* start, ssize_t stop, ssize_t step) :
step_(step),
values_minmax_(calculate_values_minmax(start, stop, step)),
sizeinfo_(calculate_arange_sizeinfo(this, start, stop, step)) {
- add_predecessor(start);
+ add_predecessor_(start);
}
ARangeNode::ARangeNode(ArrayNode* start, ssize_t stop, ArrayNode* step) :
ArrayOutputMixin(range_shape(start, stop, step)),
@@ -319,8 +319,8 @@ ARangeNode::ARangeNode(ArrayNode* start, ssize_t stop, ArrayNode* step) :
step_(step),
values_minmax_(calculate_values_minmax(start, stop, step)),
sizeinfo_(calculate_arange_sizeinfo(this, start, stop, step)) {
- add_predecessor(start);
- add_predecessor(step);
+ add_predecessor_(start);
+ add_predecessor_(step);
}
ARangeNode::ARangeNode(ArrayNode* start, ArrayNode* stop, ssize_t step) :
ArrayOutputMixin(range_shape(start, stop, step)),
@@ -329,8 +329,8 @@ ARangeNode::ARangeNode(ArrayNode* start, ArrayNode* stop, ssize_t step) :
step_(step),
values_minmax_(calculate_values_minmax(start, stop, step)),
sizeinfo_(calculate_arange_sizeinfo(this, start, stop, step)) {
- add_predecessor(start);
- add_predecessor(stop);
+ add_predecessor_(start);
+ add_predecessor_(stop);
}
ARangeNode::ARangeNode(ArrayNode* start, ArrayNode* stop, ArrayNode* step) :
ArrayOutputMixin(range_shape(start, stop, step)),
@@ -339,25 +339,25 @@ ARangeNode::ARangeNode(ArrayNode* start, ArrayNode* stop, ArrayNode* step) :
step_(step),
values_minmax_(calculate_values_minmax(start, stop, step)),
sizeinfo_(calculate_arange_sizeinfo(this, start, stop, step)) {
- add_predecessor(start);
- add_predecessor(stop);
- add_predecessor(step);
+ add_predecessor_(start);
+ add_predecessor_(stop);
+ add_predecessor_(step);
}
double const* ARangeNode::buff(const State& state) const {
- return data_ptr(state)->buff();
+ return data_ptr_(state)->buff();
}
-void ARangeNode::commit(State& state) const { data_ptr(state)->commit(); }
+void ARangeNode::commit(State& state) const { data_ptr_(state)->commit(); }
std::span ARangeNode::diff(const State& state) const {
- return data_ptr(state)->diff();
+ return data_ptr_(state)->diff();
}
bool ARangeNode::integral() const { return true; }
void ARangeNode::initialize_state(State& state) const {
- emplace_data_ptr(state, arange(state, start_, stop_, step_));
+ emplace_data_ptr_(state, arange(state, start_, stop_, step_));
}
double ARangeNode::min() const { return values_minmax_.first; }
@@ -370,7 +370,7 @@ void ARangeNode::propagate(State& state) const {
const auto [stop_old, stop_new] = std::visit(visitor, stop_);
const auto [step_old, step_new] = std::visit(visitor, step_);
- ArrayNodeStateData* ptr = data_ptr(state);
+ ArrayNodeStateData* ptr = data_ptr_(state);
// If the start or the step has changed, we need to change everything
// Alternatively if there is currently nothing in the buffer at all, we might
@@ -422,18 +422,18 @@ void ARangeNode::propagate(State& state) const {
if (ptr->diff().size()) Node::propagate(state);
}
-void ARangeNode::revert(State& state) const { data_ptr(state)->revert(); }
+void ARangeNode::revert(State& state) const { data_ptr_(state)->revert(); }
std::span