Skip to content

Commit fb60ab4

Browse files
committed
Merge branch 'master' into u/maffoo/gate-sets-dep
2 parents d2b96ed + e616710 commit fb60ab4

26 files changed

+623
-1141
lines changed

Diff for: cirq-core/cirq/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@
196196
BaseDensePauliString,
197197
bit_flip,
198198
BitFlipChannel,
199-
BooleanHamiltonian,
200199
BooleanHamiltonianGate,
201200
CCX,
202201
CCXPowGate,

Diff for: cirq-core/cirq/json_resolver_cache.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ def _class_resolver_dictionary() -> Dict[str, ObjectFactory]:
3333
from cirq.experiments import CrossEntropyResult, CrossEntropyResultDict, GridInteractionLayer
3434
from cirq.experiments.grid_parallel_two_qubit_xeb import GridParallelXEBMetadata
3535

36+
def _boolean_hamiltonian_gate_op(qubit_map, boolean_strs, theta):
37+
return cirq.BooleanHamiltonianGate(
38+
parameter_names=list(qubit_map.keys()), boolean_strs=boolean_strs, theta=theta
39+
).on(*qubit_map.values())
40+
3641
def _identity_operation_from_dict(qubits, **kwargs):
3742
return cirq.identity_each(*qubits)
3843

@@ -58,7 +63,6 @@ def _parallel_gate_op(gate, qubits):
5863
'AsymmetricDepolarizingChannel': cirq.AsymmetricDepolarizingChannel,
5964
'BitFlipChannel': cirq.BitFlipChannel,
6065
'BitstringAccumulator': cirq.work.BitstringAccumulator,
61-
'BooleanHamiltonian': cirq.BooleanHamiltonian,
6266
'BooleanHamiltonianGate': cirq.BooleanHamiltonianGate,
6367
'CCNotPowGate': cirq.CCNotPowGate,
6468
'CCXPowGate': cirq.CCXPowGate,
@@ -186,6 +190,7 @@ def _parallel_gate_op(gate, qubits):
186190
'ZPowGate': cirq.ZPowGate,
187191
'ZZPowGate': cirq.ZZPowGate,
188192
# Old types, only supported for backwards-compatibility
193+
'BooleanHamiltonian': _boolean_hamiltonian_gate_op, # Removed in v0.15
189194
'IdentityOperation': _identity_operation_from_dict,
190195
'ParallelGateOperation': _parallel_gate_op, # Removed in v0.14
191196
'SingleQubitMatrixGate': single_qubit_matrix_gate,

Diff for: cirq-core/cirq/ops/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
)
3232

3333
from cirq.ops.boolean_hamiltonian import (
34-
BooleanHamiltonian,
3534
BooleanHamiltonianGate,
3635
)
3736

Diff for: cirq-core/cirq/ops/boolean_hamiltonian.py

-96
Original file line numberDiff line numberDiff line change
@@ -30,106 +30,10 @@
3030

3131
import cirq
3232
from cirq import value
33-
from cirq._compat import deprecated_class
3433
from cirq.ops import raw_types
3534
from cirq.ops.linear_combinations import PauliSum, PauliString
3635

3736

38-
@deprecated_class(deadline='v0.15', fix='Use cirq.BooleanHamiltonianGate')
39-
@value.value_equality
40-
class BooleanHamiltonian(raw_types.Operation):
41-
"""An operation that represents a Hamiltonian from a set of Boolean functions."""
42-
43-
def __init__(
44-
self,
45-
qubit_map: Dict[str, 'cirq.Qid'],
46-
boolean_strs: Sequence[str],
47-
theta: float,
48-
):
49-
"""Builds a BooleanHamiltonian.
50-
51-
For each element of a sequence of Boolean expressions, the code first transforms it into a
52-
polynomial of Pauli Zs that represent that particular expression. Then, we sum all the
53-
polynomials, thus making a function that goes from a series to Boolean inputs to an integer
54-
that is the number of Boolean expressions that are true.
55-
56-
For example, if we were using this gate for the unweighted max-cut problem that is typically
57-
used to demonstrate the QAOA algorithm, there would be one Boolean expression per edge. Each
58-
Boolean expression would be true iff the vertices on that are in different cuts (i.e. it's)
59-
an XOR.
60-
61-
Then, we compute exp(-j * theta * polynomial), which is unitary because the polynomial is
62-
Hermitian.
63-
64-
Args:
65-
boolean_strs: The list of Sympy-parsable Boolean expressions.
66-
qubit_map: map of string (boolean variable name) to qubit.
67-
theta: The evolution time (angle) for the Hamiltonian
68-
69-
Raises:
70-
ValueError: If the any qubits are not 2D.
71-
"""
72-
if any(q.dimension != 2 for q in qubit_map.values()):
73-
raise ValueError('All qubits must be 2-dimensional.')
74-
self._qubit_map: Dict[str, 'cirq.Qid'] = qubit_map
75-
self._boolean_strs: Sequence[str] = boolean_strs
76-
self._theta: float = theta
77-
78-
def with_qubits(self, *new_qubits: 'cirq.Qid') -> 'BooleanHamiltonian':
79-
if len(self._qubit_map) != len(new_qubits):
80-
raise ValueError('Length of replacement qubits must be the same')
81-
new_qubit_map = {
82-
variable_name: new_qubit
83-
for variable_name, new_qubit in zip(self._qubit_map, new_qubits)
84-
}
85-
return BooleanHamiltonian(
86-
new_qubit_map,
87-
self._boolean_strs,
88-
self._theta,
89-
)
90-
91-
@property
92-
def qubits(self) -> Tuple[raw_types.Qid, ...]:
93-
return tuple(self._qubit_map.values())
94-
95-
def num_qubits(self) -> int:
96-
return len(self._qubit_map)
97-
98-
def _value_equality_values_(self):
99-
return self._qubit_map, self._boolean_strs, self._theta
100-
101-
def _json_dict_(self) -> Dict[str, Any]:
102-
return {
103-
'qubit_map': self._qubit_map,
104-
'boolean_strs': self._boolean_strs,
105-
'theta': self._theta,
106-
}
107-
108-
@classmethod
109-
def _from_json_dict_(cls, qubit_map, boolean_strs, theta, **kwargs):
110-
return cls(qubit_map, boolean_strs, theta)
111-
112-
def _decompose_(self):
113-
boolean_exprs = [sympy_parser.parse_expr(boolean_str) for boolean_str in self._boolean_strs]
114-
hamiltonian_polynomial_list = [
115-
PauliSum.from_boolean_expression(boolean_expr, self._qubit_map)
116-
for boolean_expr in boolean_exprs
117-
]
118-
119-
return _get_gates_from_hamiltonians(
120-
hamiltonian_polynomial_list, self._qubit_map, self._theta
121-
)
122-
123-
def _has_unitary_(self):
124-
return True
125-
126-
@property
127-
def gate(self) -> 'cirq.Gate':
128-
return BooleanHamiltonianGate(
129-
tuple(self._qubit_map.keys()), self._boolean_strs, self._theta
130-
)
131-
132-
13337
@value.value_equality
13438
class BooleanHamiltonianGate(raw_types.Gate):
13539
"""A gate that represents a Hamiltonian from a set of Boolean functions."""

Diff for: cirq-core/cirq/ops/boolean_hamiltonian_test.py

+5-29
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@
5353
'(x2 | x1) ^ x0',
5454
],
5555
)
56-
@pytest.mark.parametrize('transform', [lambda op: op, lambda op: op.gate.on(*op.qubits)])
57-
def test_circuit(boolean_str, transform):
56+
def test_circuit(boolean_str):
5857
boolean_expr = sympy_parser.parse_expr(boolean_str)
5958
var_names = cirq.parameter_names(boolean_expr)
6059
qubits = [cirq.NamedQubit(name) for name in var_names]
@@ -73,14 +72,13 @@ def test_circuit(boolean_str, transform):
7372
circuit = cirq.Circuit()
7473
circuit.append(cirq.H.on_each(*qubits))
7574

76-
with cirq.testing.assert_deprecated('Use cirq.BooleanHamiltonianGate', deadline='v0.15'):
77-
hamiltonian_gate = cirq.BooleanHamiltonian(
78-
{q.name: q for q in qubits}, [boolean_str], 0.1 * math.pi
79-
)
75+
hamiltonian_gate = cirq.BooleanHamiltonianGate(
76+
{q.name: q for q in qubits}, [boolean_str], 0.1 * math.pi
77+
)
8078

8179
assert hamiltonian_gate.num_qubits() == n
8280

83-
circuit.append(transform(hamiltonian_gate))
81+
circuit.append(hamiltonian_gate.on(*qubits))
8482

8583
phi = cirq.Simulator().simulate(circuit, qubit_order=qubits, initial_state=0).state_vector()
8684
actual = np.arctan2(phi.real, phi.imag) - math.pi / 2.0 > 0.0
@@ -89,28 +87,6 @@ def test_circuit(boolean_str, transform):
8987
np.testing.assert_array_equal(actual, expected)
9088

9189

92-
def test_with_custom_names():
93-
q0, q1, q2, q3 = cirq.LineQubit.range(4)
94-
with cirq.testing.assert_deprecated(
95-
'Use cirq.BooleanHamiltonianGate', deadline='v0.15', count=3
96-
):
97-
original_op = cirq.BooleanHamiltonian(
98-
{'a': q0, 'b': q1},
99-
['a'],
100-
0.1,
101-
)
102-
assert cirq.decompose(original_op) == [cirq.Rz(rads=-0.05).on(q0)]
103-
104-
renamed_op = original_op.with_qubits(q2, q3)
105-
assert cirq.decompose(renamed_op) == [cirq.Rz(rads=-0.05).on(q2)]
106-
107-
with pytest.raises(ValueError, match='Length of replacement qubits must be the same'):
108-
original_op.with_qubits(q2)
109-
110-
with pytest.raises(ValueError, match='All qubits must be 2-dimensional'):
111-
original_op.with_qubits(q0, cirq.LineQid(1, 3))
112-
113-
11490
def test_gate_with_custom_names():
11591
q0, q1, q2, q3 = cirq.LineQubit.range(4)
11692
gate = cirq.BooleanHamiltonianGate(

Diff for: cirq-core/cirq/protocols/json_test_data/BooleanHamiltonian.repr

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[cirq.BooleanHamiltonianGate(parameter_names=['q0'], boolean_strs=['q0'], theta=0.20160913).on(cirq.NamedQubit('q0'))]

Diff for: cirq-core/cirq/protocols/json_test_data/spec.py

-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@
190190
],
191191
deprecated={
192192
'GlobalPhaseOperation': 'v0.16',
193-
'BooleanHamiltonian': 'v0.15',
194193
'SymmetricalQidPair': 'v0.15',
195194
},
196195
tested_elsewhere=[

Diff for: cirq-google/cirq_google/engine/qcs_notebook.py

+47-60
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import os
1615
import dataclasses
1716
from typing import Union, Optional
1817

@@ -24,8 +23,7 @@
2423
Sycamore,
2524
SQRT_ISWAP_INV_PARAMETERS,
2625
PhasedFSimCharacterization,
27-
get_engine_sampler,
28-
get_engine_device,
26+
get_engine,
2927
)
3028

3129

@@ -40,9 +38,12 @@ def is_simulator(self):
4038
return isinstance(self.sampler, PhasedFSimEngineSimulator)
4139

4240

41+
# Disable missing-raises-doc lint check, since pylint gets confused
42+
# by exceptions that are raised and caught within this function.
43+
# pylint: disable=missing-raises-doc
4344
def get_qcs_objects_for_notebook(
4445
project_id: Optional[str] = None, processor_id: Optional[str] = None
45-
) -> QCSObjectsForNotebook:
46+
) -> QCSObjectsForNotebook: # pragma: nocover
4647
"""Authenticates on Google Cloud, can return a Device and Simulator.
4748
4849
Args:
@@ -57,68 +58,54 @@ def get_qcs_objects_for_notebook(
5758
An instance of DeviceSamplerInfo.
5859
"""
5960

60-
# Converting empty strings to None for form field inputs
61-
if project_id == "":
62-
project_id = None
63-
if processor_id == "":
64-
processor_id = None
65-
66-
google_cloud_signin_failed: bool = False
67-
if project_id is None:
68-
if 'GOOGLE_CLOUD_PROJECT' not in os.environ:
69-
print("No project_id provided and environment variable GOOGLE_CLOUD_PROJECT not set.")
70-
google_cloud_signin_failed = True
71-
else: # pragma: no cover
72-
os.environ['GOOGLE_CLOUD_PROJECT'] = project_id
73-
74-
# Following code runs the user through the Colab OAuth process.
75-
76-
# Checks for Google Application Default Credentials and runs
77-
# interactive login if the notebook is executed in Colab. In
78-
# case the notebook is executed in Jupyter notebook or other
79-
# IPython runtimes, no interactive login is provided, it is
80-
# assumed that the `GOOGLE_APPLICATION_CREDENTIALS` env var is
81-
# set or `gcloud auth application-default login` was executed
82-
# already. For more information on using Application Default Credentials
83-
# see https://cloud.google.com/docs/authentication/production
84-
85-
in_colab = False
61+
# Check for Google Application Default Credentials and run
62+
# interactive login if the notebook is executed in Colab. In
63+
# case the notebook is executed in Jupyter notebook or other
64+
# IPython runtimes, no interactive login is provided, it is
65+
# assumed that the `GOOGLE_APPLICATION_CREDENTIALS` env var is
66+
# set or `gcloud auth application-default login` was executed
67+
# already. For more information on using Application Default Credentials
68+
# see https://cloud.google.com/docs/authentication/production
69+
try:
70+
from google.colab import auth
71+
except ImportError:
72+
print("Not running in a colab kernel. Will use Application Default Credentials.")
73+
else:
74+
print("Getting OAuth2 credentials.")
75+
print("Press enter after entering the verification code.")
8676
try:
87-
from IPython import get_ipython
88-
89-
in_colab = 'google.colab' in str(get_ipython())
90-
91-
if in_colab:
92-
from google.colab import auth
77+
auth.authenticate_user(clear_output=False)
78+
print("Authentication complete.")
79+
except Exception as exc:
80+
print(f"Authentication failed: {exc}")
9381

94-
print("Getting OAuth2 credentials.")
95-
print("Press enter after entering the verification code.")
96-
auth.authenticate_user(clear_output=False)
97-
print("Authentication complete.")
98-
else:
99-
print(
100-
"Notebook isn't executed with Colab, assuming "
101-
"Application Default Credentials are setup."
102-
)
103-
except:
104-
pass
105-
106-
# End of Google Colab Authentication segment
107-
108-
device: cirq.Device
82+
# Attempt to connect to the Quantum Engine API, and use a simulator if unable to connect.
10983
sampler: Union[PhasedFSimEngineSimulator, QuantumEngineSampler]
110-
if google_cloud_signin_failed or processor_id is None:
84+
try:
85+
engine = get_engine(project_id)
86+
if processor_id:
87+
processor = engine.get_processor(processor_id)
88+
else:
89+
processors = engine.list_processors()
90+
if not processors:
91+
raise ValueError("No processors available.")
92+
processor = processors[0]
93+
print(f"Available processors: {[p.processor_id for p in processors]}")
94+
print(f"Using processor: {processor.processor_id}")
95+
device = processor.get_device()
96+
sampler = processor.get_sampler()
97+
signed_in = True
98+
except Exception as exc:
99+
print(f"Unable to connect to quantum engine: {exc}")
111100
print("Using a noisy simulator.")
112101
sampler = PhasedFSimEngineSimulator.create_with_random_gaussian_sqrt_iswap(
113102
mean=SQRT_ISWAP_INV_PARAMETERS,
114103
sigma=PhasedFSimCharacterization(theta=0.01, zeta=0.10, chi=0.01, gamma=0.10, phi=0.02),
115104
)
116105
device = Sycamore
117-
else: # pragma: no cover
118-
device = get_engine_device(processor_id)
119-
sampler = get_engine_sampler(processor_id, gate_set_name="sqrt_iswap")
120-
return QCSObjectsForNotebook(
121-
device=device,
122-
sampler=sampler,
123-
signed_in=not google_cloud_signin_failed,
124-
)
106+
signed_in = False
107+
108+
return QCSObjectsForNotebook(device=device, sampler=sampler, signed_in=signed_in)
109+
110+
111+
# pylint: enable=missing-raises-doc

Diff for: cirq-google/cirq_google/engine/qcs_notebook_test.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
def test_get_device_sampler():
2020
result = get_qcs_objects_for_notebook()
2121
assert result.device is cg.Sycamore
22-
assert result.signed_in is False
23-
assert type(result.sampler) is cg.PhasedFSimEngineSimulator
22+
assert not result.signed_in
23+
assert isinstance(result.sampler, cg.PhasedFSimEngineSimulator)
2424
assert result.is_simulator
2525

2626
result = get_qcs_objects_for_notebook("", "")

0 commit comments

Comments
 (0)