-
Notifications
You must be signed in to change notification settings - Fork 129
Added DFL entropy code #444
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
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
2ae79d1
Added DFL entropy code
gauravgyawali f94496d
fix the lint error
gauravgyawali 65f5f0a
fix the lint errors
gauravgyawali 80e5d1d
Addressed Nour's comments
gauravgyawali b71cf33
Address Doug's comments
gauravgyawali 088e5fb
Attempt fixing the pytest issue
gauravgyawali 02efad1
Attempt 2 fixing the pytest issue
gauravgyawali 4dfbb82
Attempt 3 fixing the pytest issue
gauravgyawali cb4e817
Attempt 4 fixing the pytest issue
gauravgyawali 60fad82
Attempt 5 fixing the pytest issue
gauravgyawali bdaed51
Attempt 6 fixing the pytest issue
gauravgyawali File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,270 @@ | ||
| # Copyright 2025 Google | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """Run experiments for measuring second Renyi entropy via randomized measurements | ||
| in 1D DFL circuits. | ||
| """ | ||
|
|
||
|
|
||
| from collections.abc import Sequence | ||
| from pathlib import Path | ||
| import pickle | ||
|
|
||
| import cirq | ||
| import numpy as np | ||
| import numpy.typing as npt | ||
|
|
||
| from recirq.dfl.dfl_enums import InitialState | ||
|
|
||
|
|
||
| def _layer_interaction( | ||
| grid: Sequence[cirq.GridQubit], | ||
| dt: float, | ||
| ) -> cirq.Circuit: | ||
| """Implements the ZZZ term of the DFL Hamiltonian | ||
| Each ZZZ term acts on matter-gauge-matter qubits. | ||
| The resulting circuit for each term looks like: | ||
| 0: ───────@────────────────────────@─────── | ||
| │ │ | ||
| 1: ───H───@───@───Rx(2 * dt)───@───@───H─── | ||
| │ │ | ||
| 2: ───────────@────────────────@─────────── | ||
|
|
||
| Args: | ||
| grid: The 1D sequence of qubits used in the experiment. | ||
| dt: The time step size for the Trotterization. | ||
|
|
||
| Returns: | ||
| cirq.Circuit for the Trotter evolution of the ZZZ term. | ||
| """ | ||
|
|
||
| moment_1 = [] | ||
| moment_2 = [] | ||
| moment_3 = [] | ||
| moment_h = [] | ||
| for i in range(0, len(grid) // 2): | ||
| q1 = grid[(2 * i)] | ||
| q2 = grid[2 * i + 1] | ||
| q3 = grid[(2 * i + 2) % len(grid)] | ||
| moment_1.append(cirq.CZ(q1, q2)) | ||
| moment_2.append(cirq.CZ(q3, q2)) | ||
| moment_h.append(cirq.H(q2)) | ||
| moment_3.append(cirq.rx(2 * dt).on(q2)) | ||
|
|
||
| return cirq.Circuit.from_moments( | ||
| cirq.Moment(moment_h), | ||
| cirq.Moment(moment_1), | ||
| cirq.Moment(moment_2), | ||
| cirq.Moment(moment_3), | ||
| cirq.Moment(moment_2), | ||
| cirq.Moment(moment_1), | ||
| cirq.Moment(moment_h), | ||
| ) | ||
|
|
||
|
|
||
| def _layer_matter_gauge_x( | ||
| grid: Sequence[cirq.GridQubit], dt: float, h: float, mu: float | ||
| ) -> cirq.Circuit: | ||
| """Implements the X rotation for the matter and gauge qubits. | ||
| The resulting circuit should look like: | ||
| 0: ──Rx(2*mu*dt)── | ||
|
|
||
| 1: ──Rx(2*h*dt)──── | ||
|
|
||
| 2: ──Rx(2*mu*dt)─── | ||
|
|
||
| Args: | ||
| grid: The 1D sequence of qubits used in the experiment. | ||
| dt: The time step size for the Trotterization. | ||
| h: The gauge field strength coefficient. | ||
| mu: The matter field strength coefficient. | ||
|
|
||
| Returns: | ||
| cirq.Circuit for the Trotter evolution of the matter and gauge fields. | ||
| """ | ||
|
|
||
| moment = [] | ||
| for i in range(len(grid)): | ||
| if i % 2 == 0: | ||
| moment.append(cirq.rx(2 * mu * dt).on(grid[i])) | ||
| else: | ||
| moment.append(cirq.rx(2 * h * dt).on(grid[i])) | ||
| return cirq.Circuit.from_moments(cirq.Moment(moment)) | ||
|
|
||
|
|
||
| def layer_floquet( | ||
| grid: Sequence[cirq.GridQubit], dt: float, h: float, mu: float | ||
| ) -> cirq.Circuit: | ||
| """Constructs a Trotter circuit for 1D Disorder-Free Localization (DFL) simulation. | ||
|
|
||
| Args: | ||
| grid: The 1D sequence of qubits used in the experiment. | ||
| dt: Trotter step size. | ||
| h: The coefficient on the gauge X term. | ||
| mu: The coefficient on the matter sigma_x term. | ||
|
|
||
| Returns: | ||
| The complete cirq.Circuit for the Trotter evolution. | ||
|
|
||
| """ | ||
|
|
||
| return _layer_interaction(grid, dt) + _layer_matter_gauge_x(grid, dt, h, mu) | ||
|
|
||
|
|
||
| def initial_state_for_entropy( | ||
| grid: Sequence[cirq.GridQubit], matter_config: InitialState | ||
| ) -> cirq.Circuit: | ||
| """Circuit for three types of initial states: | ||
| single_sector: |-> |+> |-> |+>... | ||
| superposition: |0> |+> |0> |+>... | ||
| disordered: |+/-> |-> |+/->... with randomly chosen |+> or |-> on matter sites | ||
| """ | ||
|
|
||
| moment = [] | ||
| for i in range(len(grid)): | ||
| if i % 2 == 0: | ||
| if matter_config == InitialState.SINGLE_SECTOR: | ||
| moment.append(cirq.X(grid[i])) | ||
| moment.append(cirq.H(grid[i])) | ||
|
|
||
| elif matter_config == InitialState.DISORDERED: | ||
| r = np.random.choice([0, 1]) | ||
| if r: | ||
| moment.append(cirq.X(grid[i])) | ||
| moment.append(cirq.H(grid[i])) | ||
|
|
||
| else: | ||
| moment.append(cirq.I(grid[i])) | ||
| else: | ||
| if matter_config == InitialState.DISORDERED: | ||
| moment.append(cirq.X(grid[i])) | ||
| moment.append(cirq.H(grid[i])) | ||
|
|
||
| return cirq.Circuit(moment) | ||
|
|
||
|
|
||
| def get_1d_dfl_entropy_experiment_circuits( | ||
| grid: Sequence[cirq.GridQubit], | ||
| initial_state: InitialState, | ||
| ncycles: int, | ||
| dt: float, | ||
| h: float, | ||
| mu: float, | ||
| n_basis: int = 100, | ||
| ) -> Sequence[cirq.Circuit]: | ||
| """Generate the circuit instances for the entropy experiment | ||
|
|
||
| Args: | ||
| grid: The qubits to use for the experiment. | ||
| initial_state: Which initial state, see `InitialState` enum. | ||
| ncycles: The number of Trotter steps (can be 0). | ||
| dt: Trotter step size. | ||
| h: The coefficient on the gauge X term. | ||
| mu: The coefficient on the matter sigma_x term. | ||
| n_basis: The number of random measurement bases to use. | ||
|
|
||
| Returns: | ||
| A list of the circuit instances. | ||
| """ | ||
|
|
||
| initial_circuit = initial_state_for_entropy(grid, initial_state) | ||
| circuits = [] | ||
| circ = initial_circuit + layer_floquet(grid, dt, h, mu) * ncycles | ||
|
|
||
| for _ in range(n_basis): | ||
| circ_randomized = cirq.transformers.RandomizedMeasurements()( | ||
| circ, unitary_ensemble="Clifford" | ||
| ) | ||
|
|
||
| circuits.append(circ_randomized) | ||
|
|
||
| return circuits | ||
|
|
||
|
|
||
| def run_1d_dfl_entropy_experiment( | ||
| grid: Sequence[cirq.GridQubit], | ||
| initial_states: Sequence[InitialState], | ||
| save_dir: Path, | ||
| n_cycles: Sequence[int] | npt.NDArray, | ||
| dt: float, | ||
| h: float, | ||
| mu: float, | ||
| n_basis: int = 100, | ||
| n_shots: int = 1000, | ||
| sampler: cirq.Sampler = cirq.Simulator(), | ||
| gauge_compile: bool = True, | ||
| dynamical_decouple: bool = True, | ||
| ) -> None: | ||
| """Run the 1D DFL experiment (Fig 4 of the paper). | ||
| The paper is available at: https://arxiv.org/abs/2410.06557 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a description about where this data is being saved, since it is writing to local disk. |
||
| Saves the measurement bitstrings to save_dir. | ||
|
|
||
| Data is saved in the following directory structure: | ||
| save_dir/dt{dt}/h{h}_mu{mu}/{initial_state}/cycle{n_cycle}.pickle | ||
|
|
||
| Attrs: | ||
| grid: The qubits to use for the experiment. | ||
| initial_states: The list of InitialState to use. | ||
| save_dir: The directory in which to save the results. | ||
| n_cycles: The list of number of Trotter steps to use. | ||
| dt: The Trotter step size. | ||
| h: The coefficient on the gauge X term. | ||
| mu: The coefficient on the matter sigma_x term. | ||
| n_basis: The number of random measurement bases to use. | ||
| n_shots: The number of measurement shots to use. | ||
| sampler: The cirq sampler to use. | ||
| gauge_compile: Whether to apply gauge compiling. | ||
| dynamical_decouple: Whether to apply dynamical decoupling. | ||
|
|
||
| Returns: | ||
| None | ||
| """ | ||
|
|
||
| for initial_state in initial_states: | ||
| dir_path = ( | ||
| save_dir / f"dt{dt:.2f}" / f"h{h:.2f}_mu{mu:.2f}" / initial_state.value | ||
| ) | ||
| dir_path.mkdir(parents=True, exist_ok=True) | ||
|
|
||
| for n_cycle in n_cycles: | ||
| print("Initial state:", initial_state.value, "Cycle:", n_cycle) | ||
| fname = dir_path / "cycle{}.pickle".format(n_cycle) | ||
| circuits = get_1d_dfl_entropy_experiment_circuits( | ||
| grid, | ||
| initial_state=initial_state, | ||
| ncycles=n_cycle, | ||
| dt=dt, | ||
| h=h, | ||
| mu=mu, | ||
| n_basis=n_basis, | ||
| ) | ||
|
|
||
| circuits_modified = [] | ||
| for i in range(len(circuits)): | ||
| circ_i = circuits[i] | ||
|
|
||
| if gauge_compile: | ||
| circ_i = cirq.transformers.gauge_compiling.CZGaugeTransformer(circ_i) | ||
| if dynamical_decouple: | ||
| circ_i = cirq.add_dynamical_decoupling(circ_i) | ||
| circuits_modified.append(circ_i) | ||
|
|
||
| results = sampler.run_batch(circuits, repetitions=n_shots) | ||
| bitstrings = [] | ||
| for j in range(n_basis): | ||
| bitstrings.append(results[j][0].measurements["m"]) | ||
|
|
||
| with open(fname, "wb") as myfile: | ||
| pickle.dump(bitstrings, myfile) | ||
| return None | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # Copyright 2025 Google | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import cirq | ||
|
|
||
| from recirq.dfl.dfl_entropy import layer_floquet | ||
|
|
||
|
|
||
| def test_layer_floquet_basic(): | ||
| # Create a simple grid of 4 qubits | ||
| grid = [cirq.GridQubit(0, i) for i in range(4)] | ||
| dt = 0.1 | ||
| h = 0.5 | ||
| mu = 0.3 | ||
|
|
||
| circuit = layer_floquet(grid, dt, h, mu) | ||
| # Check that the output is a cirq.Circuit | ||
| assert isinstance(circuit, cirq.Circuit) | ||
| # Check that the circuit has operations on all qubits | ||
| qubits_in_circuit = set(q for op in circuit.all_operations() for q in op.qubits) | ||
| assert set(grid) == qubits_in_circuit | ||
|
|
||
|
|
||
| def test_layer_floquet_three_qubits_structure(): | ||
| # Test for 3 qubits, manually construct the expected circuit and compare | ||
| q1, q2, q3 = cirq.LineQubit.range(3) | ||
| grid = [q1, q2, q3] | ||
| dt = 0.2 | ||
| h = 0.4 | ||
| mu = 0.6 | ||
|
|
||
| # Manually construct the expected circuit based on layer_interaction and RX layer | ||
| expected_circuit = cirq.Circuit( | ||
| cirq.H(q2), | ||
| cirq.CZ(q1, q2), | ||
| cirq.CZ(q3, q2), | ||
| cirq.rx(2 * dt).on(q2), | ||
| cirq.CZ(q3, q2), | ||
| cirq.CZ(q1, q2), | ||
| cirq.H(q2), | ||
| ) | ||
|
|
||
| expected_circuit += cirq.Circuit.from_moments( | ||
| cirq.Moment( | ||
| [ | ||
| cirq.rx(2 * mu * dt).on(q1), | ||
| cirq.rx(2 * h * dt).on(q2), | ||
| cirq.rx(2 * mu * dt).on(q3), | ||
| ] | ||
| ) | ||
| ) | ||
|
|
||
| actual_circuit = layer_floquet(grid, dt, h, mu) | ||
|
|
||
| assert actual_circuit == expected_circuit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
nit: maybe explain what this circuit is supposed to look like?
Also put a blank line before "Args:"