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

Expand SWEEP_LIKE to work for {'t': [0, 1, 3]} #2786

Merged
merged 21 commits into from
Mar 28, 2020
Merged

Expand SWEEP_LIKE to work for {'t': [0, 1, 3]} #2786

merged 21 commits into from
Mar 28, 2020

Conversation

crystalzhaizhai
Copy link
Collaborator

@crystalzhaizhai crystalzhaizhai commented Feb 24, 2020

Fixes: #2698

@googlebot googlebot added the cla: yes Makes googlebot stop complaining. label Feb 24, 2020
Copy link
Collaborator

@vtomole vtomole left a comment

Choose a reason for hiding this comment

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

Add test in sweepable_test.py

@vtomole vtomole changed the title Sweeplike Expand SWEEP_LIKE to work for {'t': [0, 1, 3]} Feb 24, 2020
Copy link
Collaborator

@vtomole vtomole left a comment

Choose a reason for hiding this comment

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

Looks like you coded your own "cartesian-producter". Let's use itertools instead. Reference https://stackoverflow.com/a/533917/5716192.

@vtomole
Copy link
Collaborator

vtomole commented Feb 25, 2020

From (https://stackoverflow.com/a/40623158/5716192). Let me know what you think.

import itertools

def dict_product(dicts):
    return (dict(zip(dicts, x)) for x in itertools.product(*dicts.values()))

print(list(dict_product({'t': [0, 2, 3]}))) # [{'t': 0}, {'t': 2}, {'t': 3}]
print(list(dict_product({'t': [0, 1], 's': [2, 3], 'r': 4}))) # Will not work for this call. Handle this case because '4' is not a list

@crystalzhaizhai
Copy link
Collaborator Author

From (https://stackoverflow.com/a/40623158/5716192). Let me know what you think.

import itertools

def dict_product(dicts):
    return (dict(zip(dicts, x)) for x in itertools.product(*dicts.values()))

print(list(dict_product({'t': [0, 2, 3]}))) # [{'t': 0}, {'t': 2}, {'t': 3}]
print(list(dict_product({'t': [0, 1], 's': [2, 3], 'r': 4}))) # Will not work for this call. Handle this case because '4' is not a list

We recreated lots of packages by ourselves, I don't know what external packages we are supposed to use.

@vtomole
Copy link
Collaborator

vtomole commented Feb 26, 2020

@crystalzhaizhai Let's use itertools here. I bet it's more efficient than what we are doing now.

@crystalzhaizhai
Copy link
Collaborator Author

@crystalzhaizhai Let's use itertools here. I bet it's more efficient than what we are doing now.

It is more efficient. However, it looks like it won't work for our test case. And it also will break our previous cases. Because it only accepts lists as values.

@vtomole
Copy link
Collaborator

vtomole commented Feb 27, 2020

That code snippet was just an example of how we should use itertools. As @Strilanc said,
Check if the input is a dictionary with list values (and possibly singletons too), and if so take the cartesian product of the various entries and turn it into a sweep.

The else will deal with previous cases.

@@ -55,7 +55,24 @@ def to_sweeps(sweepable: Sweepable) -> List[Sweep]:
if isinstance(sweepable, Sweep):
return [sweepable]
if isinstance(sweepable, dict):
return [_resolver_to_sweep(ParamResolver(cast(Dict, sweepable)))]
expandsweepable = [cast(dict, {})]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Worth a comment to explain what case this code is handling?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

   Added "change dictionary of lists to list of dictionaries of single values using Cartesian product."

cirq/study/sweepable.py Outdated Show resolved Hide resolved
@@ -83,6 +83,18 @@ def test_to_sweeps_iterable_sweeps():
assert cirq.study.to_sweeps(sweeps) == sweeps


def test_to_sweeps_dictionary_of_list():
assert cirq.study.to_sweeps({'t': [0, 2, 3]}) == \
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can use parentheses to avoid the ""

assert (cirq.study.to_sweeps({'t': [0, 2, 3]}) == 
   cirq.study.to_sweeps([{'t': 0}, {'t': 2}, {'t': 3}]))

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To keep consistent with previous test cases, I changed it to parentheses. However, check/format-incremental suggests something I feel a bit ugly. Is this what we want?

-    assert (cirq.study.to_sweeps({'t': [0, 2, 3]}) ==
-        cirq.study.to_sweeps([{'t': 0}, {'t': 2}, {'t': 3}]))
-    assert (cirq.study.to_sweeps({'t': [0, 1], 's': [2, 3], 'r': 4}) ==
-        cirq.study.to_sweeps([
-            {'t': 0, 's': 2, 'r': 4},
-            {'t': 0, 's': 3, 'r': 4},
-            {'t': 1, 's': 2, 'r': 4},
-            {'t': 1, 's': 3, 'r': 4},
-        ]))
+    assert (cirq.study.to_sweeps({'t': [0, 2, 3]}) == cirq.study.to_sweeps([{
+        't':
+        0
+    }, {
+        't':
+        2
+    }, {
+        't':
+        3
+    }]))
+    assert (cirq.study.to_sweeps({
+        't': [0, 1],
+        's': [2, 3],
+        'r': 4
+    }) == cirq.study.to_sweeps([
+        {
+            't': 0,
+            's': 2,
+            'r': 4
+        },
+        {
+            't': 0,
+            's': 3,
+            'r': 4
+        },
+        {
+            't': 1,
+            's': 2,
+            'r': 4
+        },
+        {
+            't': 1,
+            's': 3,
+            'r': 4
+        },
+    ]))

You can use parentheses to avoid the ""

assert (cirq.study.to_sweeps({'t': [0, 2, 3]}) == 
   cirq.study.to_sweeps([{'t': 0}, {'t': 2}, {'t': 3}]))

def test_to_sweeps_dictionary_of_list():
assert cirq.study.to_sweeps({'t': [0, 2, 3]}) == \
cirq.study.to_sweeps([{'t': 0}, {'t': 2}, {'t': 3}])
assert cirq.study.to_sweeps({'t': [0, 1], 's': [2, 3], 'r': 4}) == \
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 this case is quite ambiguous. You are assuming that this is a product, but it could be a zip, i.e.

cirq.study.to_sweeps([
            {'t': 0, 's': 2, 'r': 4},
            {'t': 1, 's': 3, 'r': 4},
        ])

At the very least, we need to document that multiple lists will be interpreted as a Product and not a Zip.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree. That should be more clear. Do you mind pointing me to the location of the intended change?

for shortdict in expandsweepable:
tempdict = shortdict.copy()
tempdict[key] = item
tempsweepable.append(cast(dict, tempdict))
Copy link
Collaborator

Choose a reason for hiding this comment

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

The above code is quite tricky to understand. It took me a while to figure out what this was doing, since it is repeatedly assigning the values. I would echo Victory's comment for using itertools if possible. This will also avoid recreating the entire dictionary every time there is a new list found as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The above code is quite tricky to understand. It took me a while to figure out what this was doing, since it is repeatedly assigning the values. I would echo Victory's comment for using itertools if possible. This will also avoid recreating the entire dictionary every time there is a new list found as well.

I agree using itertools is easier to read. Do you have insights which approach would run faster? I tried to profile both methods, but the test case need to be large enough to make a difference.

@@ -55,7 +56,21 @@ def to_sweeps(sweepable: Sweepable) -> List[Sweep]:
if isinstance(sweepable, Sweep):
return [sweepable]
if isinstance(sweepable, dict):
return [_resolver_to_sweep(ParamResolver(cast(Dict, sweepable)))]
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove this docstring and replace it with #.

Copy link
Collaborator

@vtomole vtomole left a comment

Choose a reason for hiding this comment

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

LGTM

@vtomole vtomole added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Mar 27, 2020
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label Mar 28, 2020
@CirqBot CirqBot merged commit 6c57f2e into quantumlib:master Mar 28, 2020
@CirqBot CirqBot removed automerge Tells CirqBot to sync and merge this PR. (If it's running.) front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. labels Mar 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Makes googlebot stop complaining.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expand SWEEP_LIKE to work for {'t': [0, 1, 3]}
5 participants