Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/feasibility #120

Merged
merged 12 commits into from
Oct 2, 2024
Merged

Conversation

jlalbers
Copy link
Contributor

@jlalbers jlalbers commented Sep 26, 2024

@arcondello adds a feasible() method to the Model class to make it easier for users to check the feasibility of a model state without manually checking the state of each constraint symbol. Addresses #82

Current work will build, but throws a segfault when I try to actually call the method in Python.

@jlalbers jlalbers marked this pull request as ready for review September 26, 2024 19:56
@jlalbers jlalbers marked this pull request as draft September 26, 2024 19:57
elif index < 0: # allow negative indexing
index += num_states

return self._graph.feasible(self.states._states[index])
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suspect it seg-faults because you need to initialize the state before calling the feasible method. This should be the cause if in your test, when calling it, you set a state and then called the feasible method right after
So you can add some python tests! :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry, I think I was unclear earlier. I meant here

self._graph.initialize_state(self.states._states[index])
return self._graph.feasible(self.states._states[index])

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh gotcha. Sorry, just my unfamiliarity with the codebase. Anything after initializing the state I'd need to clean up?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that should be it.. there are two things worth remembering here. The C++ method initialize_state() initialize all nodes in the graph (eagerly) to their value given the decision variables. If you were to check every single constraint from the python side, then it would be lazily from checking each constraint. Worth keeping this in mind in case one deals with models with very deep graphs on the objective side and simple constraints on the decisions (kinda a worst case scenario)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try doing some performance analysis and see if there's a significant cost with the eager initialization. Also could test with common problem types (i.e. models created by the generator functions) and see if it makes any difference

Copy link
Member

@arcondello arcondello Oct 2, 2024

Choose a reason for hiding this comment

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

The more I think about it, the more I think we should not expose the C++ method and just do a pure python

    def feasible(self, int index = 0):
        """Check the feasibility of the state at the input index.
        Args:
            index: index of the state to check for feasibility.
        Returns:
            Feasibility of the state.
        """
        return all(sym.state(index) for sym in self.iter_constraints())

sure there will be a minor performance hit. But this covers all of the edge cases (unitilized states, unresolved states, out of bounds indexing, no constraints, etc.)

If we later find it to be an issue, we can do the more performant thing. But IMO it won't be that much better after we do all of the edge case handling.

@jlalbers
Copy link
Contributor Author

There was a bug when checking the index against the state size (used the state_size method instead of states.size. However, I'm still getting a segfault when calling feasible with index 0 using an initialized state, i.e. the following:

...
>>> i = model.integer()
>>> c = model.constant(5)
>>> model.add_constraint(i <= c)
>>> model.feasible(0)
...
ValueError: index 0 is out of range # Expected behavior
>>> model.states.resize(1)
>>> i.set_state(0, 1) # Feasible state
>>> model.feasible(0)
Process finished with exit code 139 (interrupted by signal 11:SIGSEGV)

Could it be something to do with the State type alias used by Cython being different from the one used in the Graph C++ implementation? Could throw a segfault if the type aliases represent different objects and Graph::feasible tries to illegally access a member that ends up being at the wrong memory offset for a different object type

raise ValueError(f"index out of range: {index}")
elif index < 0: # allow negative indexing
index += num_states

Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe we can also return True if number of constraints is 0

dwave/optimization/model.pyi Outdated Show resolved Hide resolved
elif index < 0: # allow negative indexing
index += num_states

return self._graph.feasible(self.states._states[index])
Copy link
Member

@arcondello arcondello Oct 2, 2024

Choose a reason for hiding this comment

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

The more I think about it, the more I think we should not expose the C++ method and just do a pure python

    def feasible(self, int index = 0):
        """Check the feasibility of the state at the input index.
        Args:
            index: index of the state to check for feasibility.
        Returns:
            Feasibility of the state.
        """
        return all(sym.state(index) for sym in self.iter_constraints())

sure there will be a minor performance hit. But this covers all of the edge cases (unitilized states, unresolved states, out of bounds indexing, no constraints, etc.)

If we later find it to be an issue, we can do the more performant thing. But IMO it won't be that much better after we do all of the edge case handling.

Co-authored-by: Alexander Condello <arcondello@gmail.com>
@jlalbers jlalbers marked this pull request as ready for review October 2, 2024 19:09
Copy link
Member

@arcondello arcondello left a comment

Choose a reason for hiding this comment

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

LGTM, will merge when/if the CI passes

Copy link
Collaborator

@alexzucca90 alexzucca90 left a comment

Choose a reason for hiding this comment

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

LGTM

@arcondello arcondello merged commit 63f9310 into dwavesystems:main Oct 2, 2024
37 checks passed
@jlalbers jlalbers deleted the feature/feasibility branch October 2, 2024 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants