TeamGraphix / graphix

measurement-based quantum computing (MBQC) compiler and simulator
https://graphix.readthedocs.io
Apache License 2.0
55 stars 20 forks source link

remove_qubit method for faster statevec simulation #74

Closed nabe98 closed 11 months ago

nabe98 commented 12 months ago

Before submitting, please check the following:

Then, please fill in below:

Context (if applicable): ptrace method was implemented by using density matrix. But for separable states (states that measured), using DM is unnecessary.

Description of the change:

directly truncate the specified qubit.

Related issue:

73

also see that checks (github actions) pass. If lint check keeps failing, try installing black==22.8.0 as behavior seems to vary across versions.

masa10-f commented 12 months ago

Could you change the measure method in the StatevectorBackend to trace out measured qubits individually?

nabe98 commented 12 months ago

Thank you for reviewing!

Could you add more test cases? At least include the following measurement commands

  1. measure into |0>
  2. measure into |1>
  3. non-Pauli measurement

I added test_measurement_into_each_XYZ_basis to test these.

Could you change the measure method in the StatevectorBackend to trace out measured qubits individually?

I also added test_with_rand_circuit_sim to test for this simulation.

masa10-f commented 12 months ago

Thanks for your commitment! Your code looks great, but I would like to mention one minor point: the state vector backend should not depend on the pattern or circuit simulator. Therefore, it's preferable to avoid them in the unit tests. So, I have removed the random circuit check.

masa10-f commented 12 months ago

@nabe98 Before merging, can you write the changes to CHANGELOG.md?

shinich1 commented 12 months ago

@nabe98 please let me know once you updated CHANGELOG.md. Please do not merge yet. Also remember we always prefer squash and merge to keep the commit history concise on master branch :)

nabe98 commented 12 months ago

@shinich1 also updated CHANGELOG.md. Please check them!

masa10-f commented 12 months ago

I've conducted benchmarks for the new method.

[Environment] CPU: intel i7-12700K RAM: 64GB (DDR4-3200) OS: Ubuntu 20.04 (WSL)

The following graph shows a performance comparison between reduce_qubit and ptrace when simulating n (width) x n (depth) random square circuits. svbackend_comparison_square_circuit

I also benchmarked shallow (depth=1) random circuits, only changing the number of qubits. svbackend_comparison_shallow

masa10-f commented 12 months ago

I used the following random circuit generator

def random_circuit(nqubit, depth, seed=None):
    r"""Generate a random circuit.

    This function generates a random circuit with nqubit qubits and depth layers.
    The circuit is generated by CNOT gates and RZ gates.

    Parameters
    ----------
    nqubit : int
        number of qubits
    depth : int
        number of layers
    seed : int
        random seed

    Returns
    -------
    circuit : graphix.transpiler.Circuit object
        generated circuit
    """
    if seed is not None:
        random.seed(seed)
    qubit_index = [i for i in range(nqubit)]
    circuit = Circuit(nqubit)
    for _ in range(depth):
        random.shuffle(qubit_index)
        for j in range(len(qubit_index) // 2):
            circuit.cnot(qubit_index[2 * j], qubit_index[2 * j + 1])
        for j in range(len(qubit_index)):
            circuit.rz(qubit_index[j], 2 * np.pi * random.random())
    return circuit
masa10-f commented 12 months ago

I pasted the benchmark code below. I didn't perform pauli measurement processing by Pattern.perform_pauli_measurements() because Pattern.minimize_space() for a graph with gflow sometimes doesn't work well.

# %%
from graphix.generator import random_circuit
import matplotlib.pyplot as plt

from time import perf_counter

# %%
test_cases = [(i, 1) for i in range(2, 30, 2)]

score_time = []
circuit_time = []

# %%
for case in test_cases:
    nqubit, depth = case
    circuit = random_circuit(nqubit, depth)
    pattern = circuit.transpile()
    pattern.standardize()
    pattern.minimize_space()
    print(f"max space for nqubit={nqubit} circuit is ", pattern.max_space())
    start = perf_counter()
    pattern.simulate_pattern()
    end = perf_counter()
    print(f"nqubit: {nqubit}, depth: {depth}, time: {end - start}")
    score_time.append(end - start)
    start = perf_counter()
    circuit.simulate_statevector()
    end = perf_counter()
    circuit_time.append(end - start)

# %%
# plot the score_time
plt.scatter([case[0] for case in test_cases], score_time, label="score_time")
plt.scatter([case[0] for case in test_cases],
            circuit_time, label="circuit_time")
plt.xlabel("nqubit")
plt.ylabel("time (s)")
# plt.xscale("log")
plt.yscale("log")
plt.title("Time to simulate square random circuits")
plt.legend()
plt.show()

# %%
# write to file
with open("score_time.txt", "w") as f:
    for item in score_time:
        f.write(f"{item}\n")

# %%
# write circuit_time to file
with open("circuit_time.txt", "w") as f:
    for item in circuit_time:
        f.write(f"{item}\n")
shinich1 commented 11 months ago

@nabe98 can you squash and merge?