Skip to content

Commit 70b509d

Browse files
committed
tilted_square_lattice functions
Change-Id: Ie8b3fd1ac8c5289e7e85cb6fe04751a22a528558
1 parent 3308c5e commit 70b509d

File tree

6 files changed

+227
-5
lines changed

6 files changed

+227
-5
lines changed

recirq/algorithmic_benchmark_library.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import pandas as pd
77

88
from cirq_google.workflow import ExecutableSpec, QuantumExecutableGroup
9+
from recirq.otoc.loschmidt.tilted_square_lattice import (
10+
TiltedSquareLatticeLoschmidtSpec,
11+
get_all_tilted_square_lattice_executables,
12+
)
913

1014

1115
@dataclass
@@ -22,7 +26,6 @@ class AlgorithmicBenchmark:
2226
The executable family is the fully-qualified leaf-module where the code
2327
for this AlgorithmicBenchmark lives.
2428
spec_class: The ExecutableSpec subclass for this AlgorithmicBenchmark.
25-
data_class: The class which can contain ETL-ed data for this AlgorithmicBenchmark.
2629
executable_generator_func: The function that returns a QuantumExecutableGroup for a
2730
given Config.
2831
configs: A list of available `BenchmarkConfig` for this benchmark.
@@ -32,15 +35,13 @@ class AlgorithmicBenchmark:
3235
name: str
3336
executable_family: str
3437
spec_class: Type[ExecutableSpec]
35-
data_class: Type
3638
executable_generator_func: Callable[[...], QuantumExecutableGroup]
3739
configs: List['BenchmarkConfig']
3840

3941
def as_strings(self):
4042
"""Get values of this class as strings suitable for printing."""
4143
ret = {k: str(v) for k, v in dataclasses.asdict(self).items()}
4244
ret['spec_class'] = self.spec_class.__name__
43-
ret['data_class'] = self.data_class.__name__
4445
ret['executable_generator_func'] = self.executable_generator_func.__name__
4546
return ret
4647

@@ -64,6 +65,15 @@ class BenchmarkConfig:
6465

6566

6667
BENCHMARKS = [
68+
AlgorithmicBenchmark(
69+
domain='recirq.otoc',
70+
name='loschmidt.tilted_square_lattice',
71+
executable_family='recirq.otoc.loschmidt.tilted_square_lattice',
72+
spec_class=TiltedSquareLatticeLoschmidtSpec,
73+
executable_generator_func=get_all_tilted_square_lattice_executables,
74+
configs=[
75+
]
76+
),
6777
]
6878

6979

recirq/algorithmic_benchmark_library_test.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ def test_classes_and_funcs(algo):
4646
mod = import_module(algo.executable_family)
4747
assert algo.spec_class == getattr(mod, algo.spec_class.__name__), \
4848
"The spec_class must exist in the benchmark's module"
49-
assert algo.data_class == getattr(mod, algo.data_class.__name__), \
50-
"The data_class must exist in the benchmark's module"
5149
assert algo.executable_generator_func == getattr(mod, algo.executable_generator_func.__name__), \
5250
"the executable_generator_func must exist in the benchmark's module"
5351

recirq/otoc/loschmidt/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2021 Google
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from functools import lru_cache
15+
from typing import Optional
16+
17+
from cirq.protocols.json_serialization import ObjectFactory, DEFAULT_RESOLVERS
18+
from .tilted_square_lattice import (
19+
TiltedSquareLatticeLoschmidtSpec,
20+
)
21+
22+
23+
@lru_cache()
24+
def _resolve_json(cirq_type: str) -> Optional[ObjectFactory]:
25+
if not cirq_type.startswith('recirq.otoc.'):
26+
return None
27+
28+
cirq_type = cirq_type[len('recirq.otoc.'):]
29+
return {k.__name__: k for k in [
30+
TiltedSquareLatticeLoschmidtSpec,
31+
]}.get(cirq_type, None)
32+
33+
34+
DEFAULT_RESOLVERS.append(_resolve_json)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .tilted_square_lattice import *
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import itertools
2+
from dataclasses import dataclass
3+
4+
import numpy as np
5+
6+
import cirq
7+
from cirq import TiltedSquareLattice
8+
from cirq.experiments import random_rotations_between_grid_interaction_layers_circuit
9+
from cirq.protocols import dataclass_json_dict
10+
from cirq_google.workflow import QuantumExecutable, BitstringsMeasurement, QuantumExecutableGroup, \
11+
ExecutableSpec
12+
13+
14+
def create_tilted_square_lattice_loschmidt_echo_circuit(
15+
topology: TiltedSquareLattice,
16+
macrocycle_depth: int,
17+
twoq_gate: cirq.Gate = cirq.FSimGate(np.pi / 4, 0.0),
18+
rs: cirq.RANDOM_STATE_OR_SEED_LIKE = None,
19+
) -> cirq.Circuit:
20+
"""Returns a Loschmidt echo circuit using a random unitary U.
21+
22+
Args:
23+
qubits: Qubits to use.
24+
cycles: Depth of random rotations in the forward & reverse unitary.
25+
twoq_gate: Two-qubit gate to use.
26+
pause: Optional duration to pause for between U and U^\dagger.
27+
rs: Seed for circuit generation.
28+
"""
29+
30+
# Forward (U) operations.
31+
exponents = np.linspace(0, 7 / 4, 8)
32+
single_qubit_gates = [
33+
cirq.PhasedXZGate(x_exponent=0.5, z_exponent=z, axis_phase_exponent=a)
34+
for a, z in itertools.product(exponents, repeat=2)
35+
]
36+
qubits = sorted(topology.nodes_as_gridqubits())
37+
forward = random_rotations_between_grid_interaction_layers_circuit(
38+
# note: this function should take a topology probably.
39+
qubits=qubits,
40+
# note: in this function, `depth` refers to cycles.
41+
depth=4 * macrocycle_depth,
42+
two_qubit_op_factory=lambda a, b, _: twoq_gate.on(a, b),
43+
pattern=cirq.experiments.GRID_STAGGERED_PATTERN,
44+
single_qubit_gates=single_qubit_gates,
45+
seed=rs
46+
)
47+
48+
# Reverse (U^\dagger) operations.
49+
reverse = cirq.inverse(forward)
50+
51+
return (forward + reverse + cirq.measure(*qubits, key='z')).freeze()
52+
53+
54+
def _get_all_tilted_square_lattices(min_side_length=2, max_side_length=8, side_length_step=2):
55+
width_heights = np.arange(min_side_length, max_side_length + 1, side_length_step)
56+
return [TiltedSquareLattice(width, height)
57+
for width, height in itertools.combinations_with_replacement(width_heights, r=2)]
58+
59+
60+
@dataclass(frozen=True)
61+
class TiltedSquareLatticeLoschmidtSpec(ExecutableSpec):
62+
topology: TiltedSquareLattice
63+
macrocycle_depth: int
64+
instance_i: int
65+
n_repetitions: int
66+
executable_family: str = 'recirq.otoc.loschmidt.tilted_square_lattice'
67+
68+
@classmethod
69+
def _json_namespace_(cls):
70+
return 'recirq.otoc'
71+
72+
def _json_dict_(self):
73+
return dataclass_json_dict(self)
74+
75+
76+
def get_all_tilted_square_lattice_executables(
77+
n_instances=10, n_repetitions=1_000,
78+
min_side_length=2, max_side_length=8, side_length_step=2,
79+
seed=52, macrocycle_depths=None,
80+
) -> QuantumExecutableGroup:
81+
rs = np.random.RandomState(seed)
82+
if macrocycle_depths is None:
83+
macrocycle_depths = np.arange(2, 8 + 1, 2)
84+
topologies = _get_all_tilted_square_lattices(
85+
min_side_length=min_side_length,
86+
max_side_length=max_side_length,
87+
side_length_step=side_length_step,
88+
)
89+
90+
specs = [
91+
TiltedSquareLatticeLoschmidtSpec(
92+
topology=topology,
93+
macrocycle_depth=macrocycle_depth,
94+
instance_i=instance_i,
95+
n_repetitions=n_repetitions
96+
)
97+
for topology, macrocycle_depth, instance_i in itertools.product(
98+
topologies, macrocycle_depths, range(n_instances))
99+
]
100+
101+
return QuantumExecutableGroup([
102+
QuantumExecutable(
103+
spec=spec,
104+
problem_topology=spec.topology,
105+
circuit=create_tilted_square_lattice_loschmidt_echo_circuit(
106+
topology=spec.topology,
107+
macrocycle_depth=spec.macrocycle_depth,
108+
rs=rs,
109+
),
110+
measurement=BitstringsMeasurement(spec.n_repetitions)
111+
)
112+
for spec in specs
113+
])
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import numpy as np
2+
3+
import cirq
4+
# Need this exact import for monkeypatching to work (below)
5+
import recirq.otoc.loschmidt.tilted_square_lattice.tilted_square_lattice
6+
from recirq.otoc.loschmidt.tilted_square_lattice import \
7+
create_tilted_square_lattice_loschmidt_echo_circuit, TiltedSquareLatticeLoschmidtSpec, \
8+
get_all_tilted_square_lattice_executables
9+
10+
11+
def test_create_tilted_square_lattice_loschmidt_echo_circuit():
12+
topology = cirq.TiltedSquareLattice(width=3, height=2)
13+
macrocycle_depth = 2
14+
circuit = create_tilted_square_lattice_loschmidt_echo_circuit(
15+
topology=topology, macrocycle_depth=macrocycle_depth, rs=np.random.RandomState(52)
16+
)
17+
assert isinstance(circuit, cirq.FrozenCircuit)
18+
19+
assert len(circuit.all_qubits()) == topology.n_nodes
20+
assert sorted(circuit.all_qubits()) == sorted(topology.nodes_as_gridqubits())
21+
22+
edge_coloring_n = 4 # grid
23+
forward_backward = 2
24+
n_moment_per_microcycle = 2 # layer of single- and two- qubit gate
25+
measure_moment = 1
26+
extra_single_q_layer = 1
27+
assert len(circuit) == (edge_coloring_n * macrocycle_depth *
28+
n_moment_per_microcycle + extra_single_q_layer) \
29+
* forward_backward + measure_moment
30+
31+
32+
def test_tilted_square_lattice_loschmidt_spec(tmpdir):
33+
topology = cirq.TiltedSquareLattice(width=3, height=2)
34+
macrocycle_depth = 2
35+
spec1 = TiltedSquareLatticeLoschmidtSpec(
36+
topology=topology,
37+
macrocycle_depth=macrocycle_depth,
38+
instance_i=0,
39+
n_repetitions=10_000,
40+
)
41+
assert spec1.executable_family == 'recirq.otoc.loschmidt.tilted_square_lattice'
42+
43+
fn = f'{tmpdir}/spec.json'
44+
cirq.to_json(spec1, fn)
45+
spec2 = cirq.read_json(fn)
46+
assert spec1 == spec2
47+
48+
49+
def test_get_all_tilted_square_lattice_executables(monkeypatch):
50+
call_count = 0
51+
52+
def mock_get_circuit(topology: cirq.TiltedSquareLattice, macrocycle_depth: int,
53+
twoq_gate: cirq.Gate = cirq.FSimGate(np.pi / 4, 0.0),
54+
rs: cirq.RANDOM_STATE_OR_SEED_LIKE = None):
55+
nonlocal call_count
56+
call_count += 1
57+
return cirq.Circuit()
58+
59+
monkeypatch.setattr(recirq.otoc.loschmidt.tilted_square_lattice.tilted_square_lattice,
60+
"create_tilted_square_lattice_loschmidt_echo_circuit", mock_get_circuit)
61+
get_all_tilted_square_lattice_executables()
62+
n_instances = 10
63+
n_macrocycle_depths = 4 # 2,4,6,8
64+
n_side_lengths = 4 # width or height # of possibilities
65+
n_topos = n_side_lengths * (n_side_lengths + 1) / 2
66+
assert call_count == n_instances * n_macrocycle_depths * n_topos

0 commit comments

Comments
 (0)