Skip to content

Conversation

mpharrigan
Copy link
Collaborator

@mpharrigan mpharrigan commented Mar 26, 2022

plumb through some job info like job_id and job_finished_time.

cc @maffoo

plumb through some job info
@CirqBot CirqBot added the size: M 50< lines changed <250 label Mar 26, 2022
@mpharrigan mpharrigan changed the title EngineJob EngineResult Mar 29, 2022
Copy link
Contributor

@maffoo maffoo left a 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?).

Copy link
Collaborator

@dstrain115 dstrain115 left a 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.

@mpharrigan
Copy link
Collaborator Author

thanks for the review @dstrain115 and @maffoo . As a draft PR, just wanted to provide early insight into what sublassing Result would look like inside cirq_google. If the overall strategy looks good, I'll flesh out the PR with all the usual niceties

@mpharrigan mpharrigan marked this pull request as ready for review April 20, 2022 00:40
@mpharrigan mpharrigan requested review from a team, cduck, verult, vtomole and wcourtney as code owners April 20, 2022 00:40
Copy link
Contributor

@maffoo maffoo left a 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?

Comment on lines 391 to 405
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,
}
Copy link
Contributor

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:

Suggested change
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.

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'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

Copy link
Collaborator Author

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(?)'

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@mpharrigan mpharrigan mentioned this pull request Apr 20, 2022
CirqBot pushed a commit that referenced this pull request Apr 21, 2022
spun off from #5152 

It's super important when running experiments to keep track of when things happen.

cc @maffoo
@mpharrigan
Copy link
Collaborator Author

@maffoo @dstrain115 ptal; this now is based on top of #5274 and only includes the EngineResult changes.

@mpharrigan
Copy link
Collaborator Author

A lot of the test changes are putting in an update_time to the mocked quantum jobs.

@wcourtney can you verify that completed jobs will always have this field supplied?

tonybruguier pushed a commit to tonybruguier/Cirq that referenced this pull request Apr 22, 2022
spun off from quantumlib#5152 

It's super important when running experiments to keep track of when things happen.

cc @maffoo
Copy link
Contributor

@maffoo maffoo left a 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),
Copy link
Contributor

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 :-)

Copy link
Collaborator Author

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.

Copy link
Contributor

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.

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, 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

see #5301

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've removed the constructor-conversion from this PR. I recommend if we relax the serialization constraints we do that in a follow-up PR

CirqBot pushed a commit that referenced this pull request Apr 27, 2022
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`.
@mpharrigan mpharrigan added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Apr 28, 2022
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label Apr 28, 2022
@CirqBot CirqBot merged commit 4ff6264 into quantumlib:master Apr 28, 2022
@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 Apr 28, 2022
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
spun off from quantumlib#5152 

It's super important when running experiments to keep track of when things happen.

cc @maffoo
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
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`.
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
plumb through some job info like `job_id` and `job_finished_time`.

cc @maffoo
harry-phasecraft pushed a commit to PhaseCraft/Cirq that referenced this pull request Oct 31, 2024
spun off from quantumlib#5152 

It's super important when running experiments to keep track of when things happen.

cc @maffoo
harry-phasecraft pushed a commit to PhaseCraft/Cirq that referenced this pull request Oct 31, 2024
plumb through some job info like `job_id` and `job_finished_time`.

cc @maffoo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/workflow size: M 50< lines changed <250

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants