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

updates to 'dp' optimizer #119

Merged
merged 5 commits into from
Dec 7, 2019
Merged

updates to 'dp' optimizer #119

merged 5 commits into from
Dec 7, 2019

Conversation

jcmgray
Copy link
Collaborator

@jcmgray jcmgray commented Dec 2, 2019

Description

These updates the 'dp' optimizer in a few ways:

I think with these, it would make sense to make the 'optimal' algorithm point at 'dp' since with the outer product search it should now find all the same contractions, and with the cost_cap turned off it's as fast for small contractions as well. This would fix #99. Thoughts @dgasmith? Also cc @mrader1248.

Todos

  • document customizable DynamicProgramming
  • one could identify now 'standard tensor network' contractions (i.e. every index appears exactly twice) at the beginning from the index count, and possibly switch to a faster method for computing resulting indices (simply the symmetric difference of inputs)
  • use a version of 'dp' for 'optimal'?
  • have an 'auto'value for the cost_cap that simply turns it off til the contraction is quite big

Status

  • Ready to go

@codecov
Copy link

codecov bot commented Dec 2, 2019

Codecov Report

Merging #119 into master will increase coverage by 0.05%.
The diff coverage is 100%.

Copy link
Owner

@dgasmith dgasmith left a comment

Choose a reason for hiding this comment

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

A few quick comments, I need to think a bit about the logic here.


# only if s1 and s2 are disjoint
if s1 & s2 == 0:
if not s1 & s2:
Copy link
Owner

Choose a reason for hiding this comment

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

Can we collapse this and the below if to a single line to help with the indention?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah yes nice.

@@ -197,6 +197,59 @@ def test_greedy_edge_cases():
assert check_path(path, [(0, 1), (0, 2), (0, 1)])


def test_dp_edge_cases_dimension_1():
Copy link
Owner

Choose a reason for hiding this comment

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

Awesome, thanks for the extra tests!

Copy link
Contributor

Choose a reason for hiding this comment

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

Good idea to add these tests.

i_r = set()

# contraction indices:
i_cntrct = i1_cut_i2_wo_output - i_r
Copy link
Owner

Choose a reason for hiding this comment

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

Can we write out cntrct (is it contract?), will help with readability.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think it is (I didn't write these variable names originally), but yes can update.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it stands for contract. Don't know why I named it that way.. Maybe some line length issue..

Copy link
Owner

@dgasmith dgasmith left a comment

Choose a reason for hiding this comment

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

Overall LGTM with a few more docs.

@mrader1248 Do you have time to comment here?

import opt_einsum as oe

optimizer = oe.DynamicProgramming(
minimize='size', # optimize for size (rather than FLOPs)
Copy link
Owner

Choose a reason for hiding this comment

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

You may want to define size here.


>>> list(_bitmapset_indices(0b1001011))
[0, 1, 3, 6]
def _dp_calc_legs(g, all_tensors, s, inputs, i1_cut_i2_wo_output, i1_union_i2):
Copy link
Owner

Choose a reason for hiding this comment

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

Can we add short doc strings for these two functions.

else:
# if the input has any index reductions, add single contraction
inputs.append(i_reduced)
inputs_contractions.append((j,) if i_reduced != i else j)
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that my original code did not work if there was nothing more to optimise at this point. However, I find this reassignment of inputs in combination with the loop rather confusing.. I guess the original code could be fixed, if the zip would only be unpacked if the list constructed inside the zip is not empty.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes this seemed liked the simplest way to solve the 'nothing left to do' bug and also not iterate and filter the inputs twice in the same way. But certainly open to suggestions for making it more readable. Maybe just renaming the new inputs -> inputs_parsed or something?

Copy link
Contributor

Choose a reason for hiding this comment

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

To be honest, for me list comprehensions are much more expressive compared to list constructions using loops. However, at least the variable inputs should be renamed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Of course generally I agree! But when for loops can do the same thing more efficiently... I think they are the correct tool to use. I've renamed the parsed inputs to inputs_contract.

@mrader1248
Copy link
Contributor

Can you give me an example, where it is beneficial to construct outer products?

@mrader1248
Copy link
Contributor

Sorry, it took me a while to have a look at this. Overall, LGTM. And especially thanks for fixing this stupid smallest dimension 1 bug.

@jcmgray
Copy link
Collaborator Author

jcmgray commented Dec 3, 2019

Can you give me an example, where it is beneficial to construct outer products?

I've put this example in the tests (taken from you in another thread I think!):

def test_custom_dp_can_optimize_for_outer_products():
    eq = "a,b,abc->c"

    da, db, dc = 2, 2, 3
    shapes = [(da,), (db,), (da, db, dc)]

    opt1 = oe.DynamicProgramming(search_outer=False)
    opt2 = oe.DynamicProgramming(search_outer=True)

    info1 = oe.contract_path(eq, *shapes, shapes=True, optimize=opt1)[1]
    info2 = oe.contract_path(eq, *shapes, shapes=True, optimize=opt2)[1]

    assert info2.opt_cost < info1.opt_cost

Of course in practice I imagine it's almost never useful. However I think it would make sense for 'dp' to replace the optimal implementation, and if that happens then the option to be guaranteed optimal seems beneficial for testing edge cases etc. Plus for v small sizes there's no penalty hit.

@dgasmith do you have any thoughts on pointing 'optimal' to 'dp'? Maybe in a later PR.

@jcmgray
Copy link
Collaborator Author

jcmgray commented Dec 3, 2019

@dgasmith Also happy to have a go a adding more docs (e.g. each variable etc) if you think necessary.

@mrader1248
Copy link
Contributor

@jcmgray I see, I thought you found an example where constructing outer products first leads to a better solution even in the asymptotic limit. Maybe it would be good to mention in the docs that allowing for intermediate outer products can lead to horrible optimisation times?

@jcmgray
Copy link
Collaborator Author

jcmgray commented Dec 4, 2019

@jcmgray I see, I thought you found an example where constructing outer products first leads to a better solution even in the asymptotic limit

Well in the asymptotic limit where da and db are fixed but dc get's larger, indeed the outer product is still necessary here.

Maybe it would be good to mention in the docs that allowing for intermediate outer products can lead to horrible optimisation times?

I've mentioned this in the docstring for the search_outer option, but yes might be worth adding another warning to the main docs as well.

@jcmgray
Copy link
Collaborator Author

jcmgray commented Dec 6, 2019

OK, so I've added a warning to the 'dp' docs about turning on the outer product search, and factored out the single term parsing of inputs. Unless there are more comments I'll merge in a bit.

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.

Optimal path not optimal?
3 participants