MPS-MPO contraction schedule optimisation#

Here, we test an order optimisation algorithm for the MPO application. We use the algorithm which is based on the matrix bandwidth minimisation technique and is implemented in the matrex library.

[1]:
import time
import numpy as np
from tqdm import tqdm
import qecstruct as qec
import matplotlib.pyplot as plt
from mdopt.optimiser.utils import (
    ConstraintString,
    IDENTITY,
    SWAP,
    XOR_BULK,
    XOR_LEFT,
    XOR_RIGHT,
)
from examples.decoding.decoding import (
    linear_code_parity_matrix_dense,
    linear_code_constraint_sites,
    linear_code_prepare_message,
    linear_code_codewords,
    linear_code_checks,
)
from examples.decoding.decoding import (
    apply_bitflip_bias,
    apply_constraints,
    decode_message,
)
from mdopt.mps.utils import (
    create_simple_product_state,
    create_custom_product_state,
)
from mdopt.utils.utils import mpo_to_matrix
[2]:
def run_experiments(system_size, strategy):
    max_bond_dims = []
    max_entropies = []

    start_time = time.time()

    for _ in tqdm(range(NUM_EXPERIMENTS)):
        SEED = np.random.randint(1, 10**8)  # Use a new seed for each experiment
        rng = np.random.default_rng(SEED)

        NUM_CHECKS = int(
            3 * system_size / 4
        )  # Adjusted for BIT_DEGREE / CHECK_DEGREE = 3 / 4
        code = qec.random_regular_code(system_size, NUM_CHECKS, 3, 4, qec.Rng(SEED))
        code_constraint_sites = linear_code_constraint_sites(code)

        initial_codeword, perturbed_codeword = linear_code_prepare_message(
            code, PROB_ERROR, error_model=qec.BinarySymmetricChannel, seed=SEED
        )

        perturbed_codeword_state = create_custom_product_state(
            perturbed_codeword, form="Right-canonical"
        )
        perturbed_codeword_state = apply_bitflip_bias(
            perturbed_codeword_state, "All", PROB_BIAS
        )
        perturbed_codeword_state, entropies, bond_dims = apply_constraints(
            mps=perturbed_codeword_state,
            strings=code_constraint_sites,
            logical_tensors=[XOR_LEFT, XOR_BULK, SWAP, XOR_RIGHT],
            chi_max=CHI_MAX_CONTRACTOR,
            renormalise=True,
            strategy=strategy,
            silent=True,
            return_entropies_and_bond_dims=True,
        )

        max_bond_dims.append(np.max(bond_dims))
        max_entropies.append(np.max(np.abs(entropies)))

    end_time = time.time()
    elapsed_time = end_time - start_time

    return (
        np.mean(max_bond_dims),
        np.std(max_bond_dims),
        np.mean(max_entropies),
        np.std(max_entropies),
        elapsed_time,
    )
[3]:
# Define the system sizes to be considered
system_sizes = [4, 8, 12, 16, 20, 24]

# Constants
CHI_MAX_CONTRACTOR = 1e4
PROB_ERROR = 0.15
PROB_BIAS = PROB_ERROR
NUM_EXPERIMENTS = 300

# Strategy settings
strategies = ["Naive", "Optimised"]
[4]:
results = {
    strat: {
        "max_bond_dims": [],
        "bond_dims_err": [],
        "max_entropies": [],
        "entropy_err": [],
        "compute_times": [],
    }
    for strat in strategies
}

for strategy in strategies:
    for size in system_sizes:
        mean_bond_dim, std_bond_dim, mean_entropy, std_entropy, compute_time = (
            run_experiments(size, strategy)
        )
        results[strategy]["max_bond_dims"].append(mean_bond_dim)
        results[strategy]["bond_dims_err"].append(std_bond_dim)
        results[strategy]["max_entropies"].append(mean_entropy)
        results[strategy]["entropy_err"].append(std_entropy)
        results[strategy]["compute_times"].append(compute_time)
100%|██████████| 300/300 [00:01<00:00, 298.77it/s]
100%|██████████| 300/300 [00:04<00:00, 60.67it/s]
100%|██████████| 300/300 [00:18<00:00, 16.66it/s]
100%|██████████| 300/300 [02:28<00:00,  2.03it/s]
100%|██████████| 300/300 [39:17<00:00,  7.86s/it]
100%|██████████| 300/300 [14:11:48<00:00, 170.36s/it]
100%|██████████| 300/300 [00:01<00:00, 290.60it/s]
100%|██████████| 300/300 [00:04<00:00, 72.76it/s]
100%|██████████| 300/300 [00:15<00:00, 19.32it/s]
100%|██████████| 300/300 [00:43<00:00,  6.82it/s]
100%|██████████| 300/300 [11:47<00:00,  2.36s/it]
100%|██████████| 300/300 [1:39:04<00:00, 19.82s/it]
[5]:
plt.figure(figsize=(7, 5))

plt.subplot(1, 2, 1)
for strategy in strategies:
    plt.errorbar(
        system_sizes,
        results[strategy]["max_bond_dims"],
        yerr=results[strategy]["bond_dims_err"] / (np.sqrt(NUM_EXPERIMENTS)),
        label=f"{strategy} Strategy",
        fmt="-o",
        capsize=5,
        linewidth=3,
    )
plt.xlabel("System Size")
plt.ylabel("Max. Bond Dimension")
plt.title("Max. Bond Dim. vs. System Size")
plt.grid()
plt.legend()

plt.subplot(1, 2, 2)
for strategy in strategies:
    plt.errorbar(
        system_sizes,
        results[strategy]["max_entropies"],
        yerr=results[strategy]["entropy_err"] / (np.sqrt(NUM_EXPERIMENTS)),
        label=f"{strategy} Strategy",
        fmt="-o",
        capsize=5,
        linewidth=3,
    )
plt.xlabel("System Size")
plt.ylabel("Max. Absolute Entropy")
plt.title("Max. Absolute Entropy vs. System Size")
plt.grid()
plt.legend()

plt.tight_layout()
plt.show()

plt.subplot(2, 1, 1)
for strategy in strategies:
    plt.plot(
        system_sizes,
        results[strategy]["compute_times"],
        label=f"{strategy} Strategy",
        marker="o",
        linestyle="-",
        linewidth=3,
    )
plt.xlabel("System Size")
plt.ylabel("Compute Time (s)")
plt.title("Compute Time vs. System Size")
plt.grid()
plt.legend()

plt.tight_layout()
plt.show()
../_images/notebooks_maxbonddim_6_0.png
../_images/notebooks_maxbonddim_6_1.png