Skip to content

Add method for removing redundant nodes#571

Open
arcondello wants to merge 27 commits into
dwavesystems:mainfrom
arcondello:feature/node-equality-and-successor-transfer
Open

Add method for removing redundant nodes#571
arcondello wants to merge 27 commits into
dwavesystems:mainfrom
arcondello:feature/node-equality-and-successor-transfer

Conversation

@arcondello

Copy link
Copy Markdown
Member

Closes #563

AI Generation Disclosure

No AI used.

@arcondello arcondello added the enhancement New feature or request label Jun 24, 2026
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch 2 times, most recently from 4dab596 to a8040b8 Compare June 24, 2026 18:09
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from a8040b8 to 82cc356 Compare June 24, 2026 18:14
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from 56195c1 to 57638a8 Compare June 24, 2026 18:18
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from 39fb39d to 055fe62 Compare July 2, 2026 17:35
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from ee71c02 to 770a9cb Compare July 3, 2026 17:05
Comment thread dwave/optimization/include/dwave-optimization/nodes/binaryop.hpp
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from 261d2d6 to 98e4bd0 Compare July 3, 2026 18:09
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from 98e4bd0 to 47e1be7 Compare July 3, 2026 18:14
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from 90386b6 to cd107a0 Compare July 6, 2026 16:43
virtual bool equal_to(const Derived& rhs) const = 0;
};

/// Overload for EqualityMixin that does not require a class-specific equal_to()

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.

A doubt I have is whether these two overloads are different enough that they should actually be given different names 🤔

@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from 42a0edd to f7a75d1 Compare July 8, 2026 17:33
Comment thread dwave/optimization/src/nodes/lp.cpp Outdated
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from 4e887e4 to f643bca Compare July 9, 2026 22:40
@arcondello
arcondello marked this pull request as ready for review July 10, 2026 17:45
@arcondello

arcondello commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

As a note: I didn't try to be inclusive about the asserts in the replace_predecessor_() method. That way lies madness so I just went with some sanity checks.

Comment thread tests/cpp/test_graph.cpp Outdated
@arcondello
arcondello requested a review from fastbodin July 10, 2026 18:27
@arcondello
arcondello force-pushed the feature/node-equality-and-successor-transfer branch from 212caf4 to d0dd3d3 Compare July 10, 2026 19:05

@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.

Managed to work through all files excluding the test files. Thought I would upload these comments before moving onto them.

>>> model = Model()
...
>>> x = model.integer(5)
...

@fastbodin fastbodin Jul 13, 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: I find that the ... do not add much to the example. Additionally, most of the other examples do not include .... Am I missing something?

Comment thread dwave/optimization/src/graph.cpp
Comment thread dwave/optimization/src/graph.cpp
if (not dropped(uptr)) continue;

// Remove the node from its predecessor's successor vectors.
// This very leaves the node in an invalid state, very briefly

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.

Suggested change
// This very leaves the node in an invalid state, very briefly
// This leaves the node in an invalid state, very briefly

Comment thread dwave/optimization/include/dwave-optimization/nodes/binaryop.hpp
Comment on lines +378 to +379

index -= 1;

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.

    // If we haven't found the Array* we want to replace, then "normalize"
    // the index so that it looks like it would if `initial_` wasn't an array.

Comment thread dwave/optimization/src/nodes/linear_algebra.cpp
Comment thread dwave/optimization/src/nodes/manipulation.cpp
Comment thread dwave/optimization/src/nodes/naryop.cpp
bool UnaryOpNode<UnaryOp>::equal_to(const Node& rhs) const {
const auto* rhs_ptr = dynamic_cast<const UnaryOpNode*>(&rhs);
if (rhs_ptr == nullptr) return false; // not same type so not equal
return this->equal_to(*rhs_ptr);

@fastbodin fastbodin Jul 13, 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.

Confirming that I am not sleeping at the wheel. I had to do a double take here 😆

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.

Probably should change 190 to

const UnaryOpNode* rhs_ptr = dynamic_cast<const UnaryOpNode*>(&rhs);

to make it a little clearer what's happening.

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.

Usually I am fine with auto here, but, in this case, that would actually be helpful.

}
}

SECTION("predecessor replacement") {

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.

For some nodes that use the EqualityMixin without needing to implement another overload I stopped bothering to write separate tests for them, reasoning that it is already extremely well covered elsewhere.

@fastbodin fastbodin Jul 14, 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.

That is fine with me. 👍 You were correct in predicting that I would make such a comment 🤣

@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.

I have now gone through all the files. I would like to go through graph.cpp again.

The patience you had when writing these tests is a marvel.

graph.emplace_node<ArrayValidationNode>(sets.at(i));
}

THEN("node equality works as expected") {

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.

Test for DisjointListNode too?

Node* a_ptr = graph.emplace_node<BSplineNode>(x0_ptr, k, t0, c0);
Node* b_ptr = graph.emplace_node<BSplineNode>(x0_ptr, k, t0, c0);
Node* c_ptr = graph.emplace_node<BSplineNode>(x1_ptr, k, t1, c0);
Node* d_ptr = graph.emplace_node<BSplineNode>(x0_ptr, k, t0, c1);

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.

Just because BSplineNode::equal_to() also checks k, I would add

        Node* e_ptr = graph.emplace_node<BSplineNode>(x0_ptr, 3, t0, c0);

and the corresponding checks like

CHECK(not a_ptr->equal_to(*e_ptr));

Comment thread tests/cpp/nodes/test_manipulation.cpp
void ARangeNode::replace_predecessor_(ssize_t index, Node* node_ptr) {
Node::replace_predecessor_(index, node_ptr);

assert(0 <= index and static_cast<size_t>(index) < predecessors().size());

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.

This logic makes sense. Consider me convinced.

"""
return self.num_nodes()

def remove_redundant_symbols(self, time_limit_s=None):

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.

Worth adding a note in here that this is ineffective on named symbols?

auto* c0_ptr = graph.emplace_node<ConstantNode>(std::vector{0, 1, 2, 3});
auto* c1_ptr = graph.emplace_node<ConstantNode>(std::vector{7, 6, 5, 4});

auto* argsort_ptr = graph.emplace_node<MeanNode>(c0_ptr);

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.

mean_ptr

auto* c0_ptr = graph.emplace_node<ConstantNode>(std::vector{0, 1, 2, 3});
auto* c1_ptr = graph.emplace_node<ConstantNode>(std::vector{7, 6, 5, 4});

auto* argsort_ptr = graph.emplace_node<UnaryOpNode<TestType>>(c0_ptr);

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.

unaryop_ptr

Comment thread tests/cpp/test_graph.cpp
auto* y_ptr = graph.emplace_node<BinaryNode>();

auto* left_x_plus_y = graph.emplace_node<AddNode>(x_ptr, y_ptr);
auto* right_x_plus_y = graph.emplace_node<AddNode>(x_ptr, y_ptr);

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.

Suggested change
auto* right_x_plus_y = graph.emplace_node<AddNode>(x_ptr, y_ptr);
auto* right_x_plus_y = graph.emplace_node<AddNode>(y_ptr, x_ptr);

As per the variable name?

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 methods to support the removal of redundant nodes

2 participants