-
Notifications
You must be signed in to change notification settings - Fork 1.1k
EngineResult #5152
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
EngineResult #5152
Conversation
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.
This looks good to me, just a few minor comments. Happy to approve once you convert from draft (and add tests?).
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.
Fine with the general idea once we resolve comments and add tests, as maffoo mentioned.
thanks for the review @dstrain115 and @maffoo . As a draft PR, just wanted to provide early insight into what sublassing |
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.
Mostly looks good, though I have some concerns about serializing datetime.datetime
. Maybe that could be split out into a separate PR?
if isinstance(o, datetime.datetime): | ||
if o.tzinfo is not None: | ||
# coverage: ignore | ||
raise TypeError("datetime tzinfo is not serializable") | ||
return { | ||
'cirq_type': 'datetime.datetime', | ||
'year': o.year, | ||
'month': o.month, | ||
'day': o.day, | ||
'hour': o.hour, | ||
'minute': o.minute, | ||
'second': o.second, | ||
'microsecond': o.microsecond, | ||
'fold': o.fold, | ||
} |
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 would suggest serializing the timestamp instead:
if isinstance(o, datetime.datetime): | |
if o.tzinfo is not None: | |
# coverage: ignore | |
raise TypeError("datetime tzinfo is not serializable") | |
return { | |
'cirq_type': 'datetime.datetime', | |
'year': o.year, | |
'month': o.month, | |
'day': o.day, | |
'hour': o.hour, | |
'minute': o.minute, | |
'second': o.second, | |
'microsecond': o.microsecond, | |
'fold': o.fold, | |
} | |
if isinstance(o, datetime.datetime): | |
if o.tzinfo is not None: | |
# coverage: ignore | |
raise TypeError("datetime tzinfo is not serializable") | |
return { | |
'cirq_type': 'datetime.datetime', | |
'timestamp': o.timestamp(), | |
} |
For deserialization, you'll need to call fromtimestamp
instead of the class constructor:
datetime.datetime.fromtimestamp(json['timestamp'])
Note that without tzinfo, serializing datestamps with calendar values like year, month, day means the result will be interpreted in the locale timezone at both serialization and deserialization time, which may be different. The timestamp, on the other hand, is based on the unix epoch, so this will ensure that the deserialized time is the same instant, even though it might have different calendar entries after deserialization if the locale timezone is different. Also, with timestamp you don't have to assert that o.tzinfo
is None because both tz-aware and "naive" datetimes have a timestamp.
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'll split this out. I think it's really important to be able to serialize timestamps and it's crazy we're missing this functionality.
Having looked a little closer: what do you think about only allowing the serialization of UTC datetimes? when tzinfo=None, there's a ton of ambiguity and it seems harder to accurately round-trip datetimes via unix timestamp, which converts to utc
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.
naive_ts = datetime.datetime.now()
utc_ts = naive_ts.astimezone(datetime.timezone.utc)
assert naive_ts != utc_ts, 'tzinfo'
assert naive_ts.timestamp() == utc_ts.timestamp()
assert (utc_ts.day, utc_ts.hour) > (naive_ts.day, naive_ts.hour), 'at least in PDT'
re_utc = datetime.datetime.fromtimestamp(utc_ts.timestamp())
re_naive = datetime.datetime.fromtimestamp(naive_ts.timestamp())
assert re_utc == re_naive, 'roundtripping turns to utc'
assert re_utc != utc_ts, 'roundtripping loses tzinfo'
assert naive_ts == re_naive, 'works, but only because my local time isnt changing(?)'
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.
@maffoo @dstrain115 ptal; this now is based on top of #5274 and only includes the EngineResult changes. |
A lot of the test changes are putting in an @wcourtney can you verify that completed jobs will always have this field supplied? |
spun off from quantumlib#5152 It's super important when running experiments to keep track of when things happen. cc @maffoo
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.
Mostly looks good, though I have a general question about whether we should use tz-aware datetimes consistently, since that's what we support for serialization.
def test_engine_result(): | ||
res = cg.EngineResult( | ||
job_id='my_job_id', | ||
job_finished_time=datetime.datetime(2022, 4, 1, 1, 23, 45), |
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.
This naive datetime will not be serializable, right? In that case I'd suggest adding a timezone, so we model best practices. Or maybe we should support serializing naive datetimes :-)
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.
In the constructor of cg.EngineResult
, I canonicalize everything to "aware" utc datetimes, so this will still function.
Do you think we should only allow "aware" datetimes in EngineResult? Or just take whatever is input and face an error on serialization?
In any event; I'll change the test fixtures to use aware datetimes.
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.
It's probably worth a broader discussion how we want to handle datetimes. My personal opinion would be to allow naive and aware datetimes in serialization as well, since we can convert either to a timestamp in a well-defined way, just like we can normalize them in the EngineResult
constructor. I agree for ergonomics it's nice to handle either when constructing EngineResult
, but just as it's potentially surprising to not be able to do round-trip equality if you serialize a naive datetime and get back an aware one, it also feels suprising to change things when contructing an object:
dt = datetime.datetime.now()
res = cg.EngineResult(job_finished_time=dt, ...)
dt == res.job_finished_time # False!
That's not such a big deal here since people will generally not be constructing EngineResult
instances manually.
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, I agree with everything you say. Let me see if I can write down the options in a way everyone can agree on and then we can pick one.
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.
see #5301
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've removed the constructor-conversion from this PR. I recommend if we relax the serialization constraints we do that in a follow-up PR
Add `run_start_time` and `run_end_time` to `cg.workflow.execute()` `SharedRuntimeInfo`. This gives you an overview of when a given workflow run started and completed. This is complementary to #5152 which will record a timestamp for each `cirq.Result`.
spun off from quantumlib#5152 It's super important when running experiments to keep track of when things happen. cc @maffoo
Add `run_start_time` and `run_end_time` to `cg.workflow.execute()` `SharedRuntimeInfo`. This gives you an overview of when a given workflow run started and completed. This is complementary to quantumlib#5152 which will record a timestamp for each `cirq.Result`.
plumb through some job info like `job_id` and `job_finished_time`. cc @maffoo
spun off from quantumlib#5152 It's super important when running experiments to keep track of when things happen. cc @maffoo
plumb through some job info like `job_id` and `job_finished_time`. cc @maffoo
plumb through some job info like
job_id
andjob_finished_time
.cc @maffoo