Repository: quantumlib/Cirq

Issue:
[cirqflow] `KeyValueExecutableSpec` should provide a `to_dict` method / override `__getitem__`
**Is your feature request related to a use case or problem? Please describe.**
`cg.KeyValueExecutableSpec` provides a nice `from_dict()` method to convert a dict into a `Tuple[Tuple[str, Any], ...]` which is hashable. This is useful when constructing the executable spec. However, using the executable spec during analysis of the results forces one to use the stored tuples, which is cumbersome.

**Describe the solution you'd like**
The class should provide a similar `to_dict` method which can convert the stored `key_value_pairs` to a dictionary and return -- which are much easier to work with.  Though the method would be a simple `return dict(self.key_value_pairs)`, there might be some value in explicitly having it on the class.  We can also consider providing a custom `__getitem__` method. 

**What is the urgency from your perspective for this issue? Is it blocking important work?**
P1 - I need this no later than the next release (end of quarter)

cc @mpharrigan 


Discussion:
sounds like a good idea to me
I agree with @mpharrigan. Added triage/discuss to decide which options to go with.
Discussed in cirq sync: We should have a `to_dict` method. Probably also investigate whether the class needs to be immutable since cirq json serialization can handle dict to sorted tuples conversion. 

Another heads up here is that dict -> tuple conversion should sort tuples to guarantee stability. In this class, we do not currently do the sorting. cc @mpharrigan 
dictionaries are ordered in modern python. it's not clear to me whether we should be sorting.
They are insertion ordered, not sorted based on keys. One counter intuitive example due to this is:

```python
> d1 = dict.fromkeys(['a', 'b'])
> d2 = dict.fromkeys(['b', 'a'])
> assert d1 == d2
> k1 = cg.KeyValueExecutableSpec.from_dict(d1, executable_family='test')
> k2 = cg.KeyValueExecutableSpec.from_dict(d2, executable_family='test')
> assert k1 != k2  # k1 == k2 should ideally be True. 
```
currently keyvaluespec has these semantics

```python
KVSpec(stuff=(('a', 1), ('b', 2))) != KVSpec(stuff=(('b', 2), ('a', 1)))
```

so we should "fix" this as well. 

Resolving patch:
diff --git a/cirq-google/cirq_google/workflow/quantum_executable.py b/cirq-google/cirq_google/workflow/quantum_executable.py
--- a/cirq-google/cirq_google/workflow/quantum_executable.py
+++ b/cirq-google/cirq_google/workflow/quantum_executable.py
@@ -53,6 +53,9 @@ class KeyValueExecutableSpec(ExecutableSpec):
     executable_family: str
     key_value_pairs: Tuple[Tuple[str, Any], ...] = ()
 
+    def to_dict(self) -> Dict[str, Any]:
+        return dict(self.key_value_pairs)
+
     @classmethod
     def _json_namespace_(cls) -> str:
         return 'cirq.google'


Test patch:
diff --git a/cirq-google/cirq_google/workflow/quantum_executable_test.py b/cirq-google/cirq_google/workflow/quantum_executable_test.py
--- a/cirq-google/cirq_google/workflow/quantum_executable_test.py
+++ b/cirq-google/cirq_google/workflow/quantum_executable_test.py
@@ -63,6 +63,18 @@ def test_kv_executable_spec():
         hash(KeyValueExecutableSpec(executable_family='', key_value_pairs=[('name', 'test')]))
 
 
+def test_dict_round_trip():
+    input_dict = dict(name='test', idx=5)
+
+    kv = KeyValueExecutableSpec.from_dict(
+        input_dict, executable_family='cirq_google.algo_benchmarks.example'
+    )
+
+    actual_dict = kv.to_dict()
+
+    assert input_dict == actual_dict
+
+
 def test_kv_repr():
     kv = _get_example_spec()
     cirq.testing.assert_equivalent_repr(kv, global_vals={'cirq_google': cirq_google})