Repository: quantumlib/Cirq

Issue:
A wrapper function for device calibration 
From @mpharrigan: A wrapper function for getting device calibration that doesn't require creating an Engine object


Discussion:

Resolving patch:
diff --git a/cirq/google/__init__.py b/cirq/google/__init__.py
--- a/cirq/google/__init__.py
+++ b/cirq/google/__init__.py
@@ -34,6 +34,7 @@
     ProtoVersion,
     QuantumEngineSampler,
     get_engine,
+    get_engine_calibration,
     get_engine_device,
     get_engine_sampler,
 )
diff --git a/cirq/google/engine/__init__.py b/cirq/google/engine/__init__.py
--- a/cirq/google/engine/__init__.py
+++ b/cirq/google/engine/__init__.py
@@ -21,6 +21,7 @@
 from cirq.google.engine.engine import (
     Engine,
     get_engine,
+    get_engine_calibration,
     get_engine_device,
     ProtoVersion,
 )
diff --git a/cirq/google/engine/engine.py b/cirq/google/engine/engine.py
--- a/cirq/google/engine/engine.py
+++ b/cirq/google/engine/engine.py
@@ -563,5 +563,26 @@ def get_engine_device(processor_id: str,
                       project_id: Optional[str] = None,
                       gatesets: Iterable[sgs.SerializableGateSet] = ()
                      ) -> 'cirq.Device':
+    """Returns a `Device` object for a given processor.
+
+    This is a short-cut for creating an engine object, getting the
+    processor object, and retrieving the device.  Note that the
+    gateset is required in order to match the serialized specification
+    back into cirq objects.
+    """
     return get_engine(project_id).get_processor(processor_id).get_device(
         gatesets)
+
+
+def get_engine_calibration(
+        processor_id: str,
+        project_id: Optional[str] = None,
+) -> Optional['cirq.google.Calibration']:
+    """Returns calibration metrics for a given processor.
+
+    This is a short-cut for creating an engine object, getting the
+    processor object, and retrieving the current calibration.
+    May return None if no calibration metrics exist for the device.
+    """
+    return get_engine(project_id).get_processor(
+        processor_id).get_current_calibration()


Test patch:
diff --git a/cirq/google/engine/engine_test.py b/cirq/google/engine/engine_test.py
--- a/cirq/google/engine/engine_test.py
+++ b/cirq/google/engine/engine_test.py
@@ -772,3 +772,37 @@ def test_get_engine_device(get_processor):
         device.validate_operation(cirq.X(cirq.GridQubit(1, 2)))
     with pytest.raises(ValueError):
         device.validate_operation(cirq.Y(cirq.GridQubit(0, 0)))
+
+
+_CALIBRATION = qtypes.QuantumCalibration(
+    name='projects/a/processors/p/calibrations/1562715599',
+    timestamp=_to_timestamp('2019-07-09T23:39:59Z'),
+    data=_to_any(
+        Merge(
+            """
+    timestamp_ms: 1562544000021,
+    metrics: [
+    {
+        name: 't1',
+        targets: ['0_0'],
+        values: [{
+            double_val: 321
+        }]
+    }, {
+        name: 'globalMetric',
+        values: [{
+            int32_val: 12300
+        }]
+    }]
+""", v2.metrics_pb2.MetricsSnapshot())))
+
+
+@mock.patch(
+    'cirq.google.engine.engine_client.EngineClient.get_current_calibration')
+def test_get_engine_calibration(get_current_calibration):
+    get_current_calibration.return_value = _CALIBRATION
+    calibration = cirq.google.get_engine_calibration('rainbow', 'project')
+    assert calibration.timestamp == 1562544000021
+    assert set(calibration.keys()) == {'t1', 'globalMetric'}
+    assert calibration['t1'][(cirq.GridQubit(0, 0),)] == [321.0]
+    get_current_calibration.assert_called_once_with('project', 'rainbow')
diff --git a/rtd_docs/docs_coverage_test.py b/rtd_docs/docs_coverage_test.py
--- a/rtd_docs/docs_coverage_test.py
+++ b/rtd_docs/docs_coverage_test.py
@@ -74,10 +74,10 @@ def test_public_values_equals_documented_values():
         if not fullname.startswith('cirq.contrib.')
     }
     assert not unlisted, (
-        'Public class/method/value not listed in docs/api.rst:'
+        'Public class/method/value not listed in rtd_docs/api.rst:'
         '\n    ' + '\n    '.join(sorted(unlisted)))
     assert not hidden, (
-        'Private or non-existent class/method/value listed in docs/api.rst:'
+        'Private or non-existent class/method/value listed in rtd_docs/api.rst:'
         '\n    ' + '\n    '.join(sorted(hidden)))