-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add methods to serialize multi-programs in cirq_google #7678
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
base: main
Are you sure you want to change the base?
Add methods to serialize multi-programs in cirq_google #7678
Conversation
dstrain115
commented
Sep 30, 2025
- Adds a new Message in the Program proto for KeyedCircuit which can store multiple circuits indexed by parameters.
- This also adds a pair of functions in CircuitSerializer called serialize_multi_program and deserialize_multi_program.
- These can serialize a list of circuits, a map of circuits with string keys, or a function that returns a circuit (with a corresponding sweep object that specifies how to call the function).
- Adds a new Message in the Program proto for KeyedCircuit which can store multiple circuits indexed by parameters. - This also adds a pair of functions in CircuitSerializer called serialize_multi_program and deserialize_multi_program. - These can serialize a list of circuits, a map of circuits with string keys, or a function that returns a circuit (with a corresponding sweep object that specifies how to call the function).
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7678 +/- ##
=======================================
Coverage 99.37% 99.38%
=======================================
Files 1085 1084 -1
Lines 96937 97029 +92
=======================================
+ Hits 96331 96428 +97
+ Misses 606 601 -5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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 wonder if the creation of circuits from sweep parameters and a circuit factory should be moved to a separate function so we could simplify what argument types are accepted in serialize_multi_program
. The type signature there could also accept Iterable[AbstractCircuit]
and Iterable[tuple[str, AbstractCircuit]]
so that serialize_multi_program
could take a generator function output.
results. | ||
Raises: | ||
NotImplementedError: If the program is of a type that is supported. |
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.
typo: supported
--> not supported
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.
Done.
| Callable[..., cirq.AbstractCircuit] | ||
| Callable[..., Mapping[str, cirq.AbstractCircuit]] |
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.
IIUC, the callable arguments are used to generate circuits from the sweep parameters, essentially translating them to either a sequence or mapping of circuits, ie, the other two parameter types. The sweep
argument is not serialized at all, it is only used to provide arguments for the callable.
How about factoring the circuit generation to a separate function, say circuits_from_sweep
which would return either a sequence or mapping of Circuits?
If memory overhead is of concern, circuits_from_sweep
could be a generator function and the multi_program
argument here could be adjusted to accept Iterable[AbstractCircuit] | Iterable[tuple[str, AbstractCircuit]]
.
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 separated this out to serialize_circuit_function(). I don't think having circuits_from_sweep is worth it, since that is not intended functionality. You'd just want to serialize them immediately. What do you think?
raise ValueError(f'Function returned unrecognized type: {type(circuit_or_map)}') | ||
for key, circuit in circuit_tuples: | ||
new_program = msg.keyed_circuits.add() | ||
if key is not None: |
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.
Nit - this should be always True looking at the code above. Maybe remove?
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.
Done
("", circuit_or_map) | ||
] | ||
elif isinstance(circuit_or_map, Mapping): | ||
circuit_tuples = list(circuit_or_map.items()) |
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.
circuit_tuples
from different sweep points can have duplicate keys
, so the Program.keyed_circuits sequence may get non-unique KeyedCircuit.key
values as well.
If that is not an issue, perhaps the serialize_multi_program
should also accept an iterable of (key, Circuit)
pairs?
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 not totally sure I understand the concern. The combination of key and args should always be unique. I am not sure that the key needs to be unique on its own.
def test_multi_programs_list() -> None: | ||
"""Test serialize_multi_program with a list of circuits.""" | ||
serializer = cg.CircuitSerializer() | ||
circuits = list(_create_circuit(i, 1.0) for i in range(10)) |
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.
Nit - consider replacing with list comprehension here and below -
circuits = list(_create_circuit(i, 1.0) for i in range(10)) | |
circuits = [_create_circuit(i, 1.0) for i in range(10)] |
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.
Done.
// def circuit_fn(freq_GHz: float, num_cycles: int) -> Circuit | ||
// would have the two entries: "freq_GHz" and "num_cycles" | ||
map<string, Arg> args = 1; | ||
|
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.
Nit - git diff --check main...
shows some trailing blanks here.
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.
Done.