-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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
ENH: Allow QAP to accept adjacency matrices of different sizes #13034
Conversation
@mdhaber please take a look when you have a chance! |
scipy/optimize/_qap.py
Outdated
@@ -507,6 +519,29 @@ def _quadratic_assignment_faq(A, B, | |||
return OptimizeResult(res) | |||
|
|||
|
|||
def _adj_pad(A, B, method): | |||
# pads the matrix with less nodes such that A & B are same size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by "less nodes"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would "fewer nodes" be more clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be the better word, but "fewer" relative to what?
Do you mean it pads the smaller of A and B to be the same sized as the larger?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Your wording is more clear, so I'll replace what I had written with that.
scipy/optimize/_qap.py
Outdated
B = 2 * B - np.ones((B_n, B_n)) | ||
|
||
if A.shape[0] == n[0]: | ||
A = pad(A, n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A = pad(A, n) | |
A = np.pad(A, (0, n[1]-n[0])) |
scipy/optimize/_qap.py
Outdated
def pad(X, n): | ||
X_pad = np.zeros((n[1], n[1])) | ||
X_pad[: n[0], : n[0]] = X | ||
return X_pad |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def pad(X, n): | |
X_pad = np.zeros((n[1], n[1])) | |
X_pad[: n[0], : n[0]] = X | |
return X_pad |
scipy/optimize/_qap.py
Outdated
if A.shape[0] == n[0]: | ||
A = pad(A, n) | ||
else: | ||
B = pad(B, n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
B = pad(B, n) | |
B = np.pad(B, (0, n[1]-n[0])) |
A_n = A.shape[0] | ||
B_n = B.shape[0] | ||
n = np.sort([A_n, B_n]) | ||
if method == "adopted": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scipy/optimize/_qap.py
Outdated
@@ -507,6 +519,29 @@ def _quadratic_assignment_faq(A, B, | |||
return OptimizeResult(res) | |||
|
|||
|
|||
def _adj_pad(A, B, method): | |||
# pads the matrix with less nodes such that A & B are same size | |||
# schemes according to section 2.5 of [2] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
n = 50 | ||
p = 0.4 | ||
G1 = _er_matrix(n, p) | ||
G2 = G1[: (n - 1), : (n - 1)] # remove two nodes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
G2 = G1[: (n - 1), : (n - 1)] # remove two nodes | |
G2 = G1[: -1, -1] # remove two nodes |
Doesn't this remove one node?
@mdhaber let us know if you need anything else from else prior to reviewing this, thanks! |
@jovo Thanks for your patience. It would help if we could improve the testing.
The logic looks right; I'm just trying to think of ways we can confirm that this will do the right thing. |
@mdhaber no worries, I understand the (often thankless) difficulties of maintaining high quality packages. I'll talk to @asaadeldin11 and we'll address all those concerns right away, thanks! |
padding and low-density subgraphs of `B`. | ||
|
||
"naive" : matches `A` to the best fitting subgraph of `B`. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe point the reader to more detail in Section 2.5 of [2].
Thanks for the feedback!
This is a relatively weak test, it was basically just a sanity check to make sure nothing was broken. I agree that we need stronger tests, please see my suggestions below.
No particular reason, just that it is a simple graph model. The main strength of this padding is to find subgraphs in a larger graph, so it's applications are mainly in the graph matching side of things, though it does also work with the QAP
Sure. I can generate three 25 x 25 ER graphs of different probabilities (say 0.6, 0.1, 0.2), and construct A as the block graph [[0.6, 0.1],[0.1, 0.2]] and B as just 0.6, so A is 50 x 50 and B is 25 x 25. Then i would expect the nodes from 1-25 to map to each other from A to B.
Yes, for the above test I can make sure that the objective function value is between 0 and 25^2 as well
sure. I can make small graphs of different sizes and manually pad one, then just make sure they get the same objective function value, if you think that's sufficient?
Since the padding section of the SGM paper is pretty short, the only result they show is essentially figure 3, which I recreated here. This is more of a structural/visual result, so I'm not sure if it would be appropriate to put it in the tests.
Sounds good on the above three.
@mdhaber Let me know if have comments on my above suggestions, thanks! |
Thanks for the responses. Yeah, I think all that would help. Only comment is about:
Sure. I suppose I was hoping for something a little stronger. Isn't this algorithm supposed to get within twice the optimal objective function value or something? Maybe that's only for the graphs being the same size? |
The actual objective function range in the tests will be much more narrow than this, I just wasn't exactly sure what those values would be off the top of my head. I just meant that the range would be between 0 and 25^2 since those are the absolute bounds (in the tests it will likely be something like 250-350 to be more exact) |
I know. I just wonder whether you can provide a bound from theory that is tighter than the (what I called trivial) absolute bound. |
@asaadeldin11 @jovo Were you still interested in this? I may be able to help finish it up now if the new tests are stronger. On the other hand, we haven't received any bug reports, enhancement requests, or attention from other maintainers for QAP, so I'm not sure how much the enhancement would be used. We could consider keeping this on the backburner until there is more interest. |
@bdpedigo what do you think? you are in charge of this now :) |
Reference issue
What does this implement/fix?
This PR implements "padding" to the
quadratic_assignment()
function in scipy.optimize, allowing users to input matricesA
andB
of different sizes. Two different padding schemes are implemented here, and the user is given the option to choose between the two.adopted
(default) padding matches the appropriate induced subgraphs, andnaive
matches the appropriate subgraphs. More information can be found in section 2.5 of "Seeded Graph Matching"Additional information