def pair_double_sel(ps, pm, pg, eta, a0, a1, theta):
    """
    Create a entangled pair using the double selection
    distillation protocol. The EPL protocol is used in the intermediate
    entangled pairs used.

    Parameters
    ----------
    ps : single qubit gate error rate.
    pm : measurement error rate.
    pg : two qubit gate error rate.
    eta : detection efficiency.
    a0 : extra environmental error when electron spin is being operated.
    a1 : default environmental error.
    theta : determines how the states are initialized when generating remote
            entanglement.
    """
    cb = circuit_block.Blocks(ps, pm, pg, eta, a0, a1, theta)
    epl = pair_EPL(ps, pm, pg, eta, a0, a1, theta)

    double_sel = circuit.Circuit(a0=a0, a1=a1,
                                 circuit_block=cb.double_selection_ops,
                                 targets=[0, 1], ancillas1=[2, 3],
                                 ancillas2=[4, 5], sigma="Z")
    double_sel.add_circuit(circuit_block=cb.swap_pair,
                           pair=[0, 1])
    double_sel.add_circuit(circuit_block=epl.append_circuit)
    double_sel.add_circuit(circuit_block=epl.append_circuit)
    double_sel.add_circuit(circuit_block=cb.start_epl)

    return double_sel
def ghz2_double(ps, pm, pg, eta, a0, a1, theta):
    """
    GHZ state of weigth 2 created using 2 Bell pairs
    generated using the double selection protocol.

    Parameters
    ----------
    ps : (scalar) single qubit gate error rate.
    pm : (scalar) measurement error rate.
    pg : (scalar) two qubit gate error rate.
    eta : (scalar) detection efficiency.
    a0 : (scalar) extra environmental error when electron spin is being operated.
    a1 : (scalar) default environmental error.
    theta : (scalar) determines how the states are initialized when generating remote
            entanglement.
    """
    cb = circuit_block.Blocks(ps, pm, pg, eta, a0, a1, theta)
    pair = pair_double_sel(ps, pm, pg, eta, a0, a1, theta)

    ghz = circuit.Circuit(a0=a0, a1=a1,
                          circuit_block=cb.single_selection_ops,
                          targets=[0, 1], ancillas=[2, 3], sigma="Z")
    ghz.add_circuit(circuit_block=cb.swap_pair,
                    pair=[0, 1])
    ghz.add_circuit(circuit_block=pair.run_parallel)

    return ghz
def ghz4_epl(ps, pm, pg, eta, a0, a1, theta):
    """
    GHZ state of weigth 4 created using 4 Bell pairs
    generated using the EPL protocol.

    Parameters
    ----------
    ps : (scalar) single qubit gate error rate.
    pm : (scalar) measurement error rate.
    pg : (scalar) two qubit gate error rate.
    eta : (scalar) detection efficiency.
    a0 : (scalar) extra environmental error when electron spin is being operated.
    a1 : (scalar) default environmental error.
    theta : (scalar) determines how the states are initialized when generating remote
            entanglement.
    """
    # Circuits are assemebled in reversed order
    cb = circuit_block.Blocks(ps, pm, pg, eta, a0, a1, theta)
    epl = pair_EPL(ps, pm, pg, eta, a0, a1, theta)


    # Phase 3 - Create GHZ
    # Perform the measurements
    ghz = circuit.Circuit(a0=a0, a1=a1,
                          circuit_block=cb.collapse_ancillas_GHZ,
                          ghz_size=4,
                          measure_pos=[4, 5, 6, 7])
    # Apply two qubit gates in the nodes
    ghz.add_circuit(circuit_block=cb.two_qubit_gates, controls=[1, 3, 0, 2],
                    targets=[4, 5, 6, 7], sigma="X")

    # Phase 2 Create last two pairs
    pair = circuit.Circuit(a0=a0, a1=a1, circuit_block=cb.swap_pair,
                           pair=[0, 1])
    pair.add_circuit(circuit_block=cb.start_epl)
    wrap_EPL_parallel = circuit.Circuit(a0=a0, a1=a1,
                                        circuit_block=pair.run_parallel)

    ghz.add_circuit(circuit_block=wrap_EPL_parallel.append_circuit)

    # Phase 1 Create initial two pairs

    ghz.add_circuit(circuit_block=epl.run_parallel)

    return ghz
def pair_EPL(ps, pm, pg, eta, a0, a1, theta):
    """
    Create a entangled pair using the EPL protocol.

    Parameters
    ----------
    ps : single qubit gate error rate.
    pm : measurement error rate.
    pg : two qubit gate error rate.
    eta : detection efficiency.
    a0 : extra environmental error when electron spin is being operated.
    a1 : default environmental error.
    theta : determines how the states are initialized when generating remote
            entanglement.
    """
    cb = circuit_block.Blocks(ps, pm, pg, eta, a0, a1, theta)
    epl = circuit.Circuit(a0=a0, a1=a1,
                          circuit_block=cb.start_epl)
    return epl
def ghz3_single_simple(ps, pm, pg, eta, a0, a1, theta):
    """
    GHZ state of weigth 3 created using 2 Bell pairs
    generated using the EPL protocol.

    Parameters
    ----------
    ps : (scalar) single qubit gate error rate.
    pm : (scalar) measurement error rate.
    pg : (scalar) two qubit gate error rate.
    eta : (scalar) detection efficiency.
    a0 : (scalar) extra environmental error when electron spin is being operated.
    a1 : (scalar) default environmental error.
    theta : (scalar) determines how the states are initialized when generating remote
            entanglement.
    """
    # Circuits are assemebled in reversed order
    cb = circuit_block.Blocks(ps, pm, pg, eta, a0, a1, theta)
    pair = pair_single_sel(ps, pm, pg, eta, a0, a1, theta)


    # Phase 3 - Create GHZ
    # Perform the measurements
    ghz = circuit.Circuit(a0=a0, a1=a1,
                          circuit_block=cb.collapse_ancillas_GHZ,
                          ghz_size=3,
                          measure_pos=[2])
    # Apply two qubit gates in the nodes
    ghz.add_circuit(circuit_block=cb.two_qubit_gates, controls=[1],
                    targets=[2], sigma="X")

    # Phase 2 Create second pair
    ghz.add_circuit(circuit_block=cb.swap_pair,
                    pair=[0, 1])
    ghz.add_circuit(circuit_block=pair.append_circuit)

    # Phase 1 Create initial pair
    ghz.add_circuit(circuit_block=pair.start)

    return ghz
def ghz2_bk(ps, pm, pg, eta, a0, a1, theta):
    """
    GHZ state of weigth 2 created using 2 Bell pairs
    generated using the double selection protocol.

    Parameters
    ----------
    ps : (scalar) single qubit gate error rate.
    pm : (scalar) measurement error rate.
    pg : (scalar) two qubit gate error rate.
    eta : (scalar) detection efficiency.
    a0 : (scalar) extra environmental error when electron spin is being operated.
    a1 : (scalar) default environmental error.
    theta : (scalar) determines how the states are initialized when generating remote
            entanglement.
    """
    cb = circuit_block.Blocks(ps, pm, pg, eta, a0, a1, theta)
    pair = pair_double_sel(ps, pm, pg, eta, a0, a1, theta)

    ghz = circuit.Circuit(a0=a0, a1=a1,
                          circuit_block=cb.start_BK)

    return ghz
# Determine parameters
# NOTE: Realistic paramters
ps = 0.006
pm = 0.006
pg = 0.006
a0 = 83.33
a1 = 1 / 3.
eta = (0.1) * (0.03) * (0.8)
theta = 0.63

# Number of iterations for a average
iterations = 300
ignore_number = int(iterations / 100)

cb = circb.Blocks(ps, pm, pg, eta, a0, a1, theta)

# First assemeble the small single selection circuit
single_sel = circ.Circuit(a0=a0, a1=a1, circuit_block=cb.start_epl)
single_sel.add_circuit(circuit_block=cb.swap_pair, pair=[0, 1])
single_sel.add_circuit(circuit_block=cb.single_selection,
                       operation_qubits=[0, 1],
                       sigma="Z")

# Initialize objects and define references
rho_ref = qt.bell_state('00') * qt.bell_state('00').dag()
rho_ref2 = qt.bell_state('01') * qt.bell_state('01').dag()
ghz_ref = qt.ghz_state(4) * qt.ghz_state(4).dag()
ghz3_ref = qt.ghz_state(3) * qt.ghz_state(3).dag()
stab = stabilizer.Stabilizer(0, 0, 0)
eta = (0.1)*(0.03)*(0.8)
theta = 0.63

# Number of iterations for a average
iterations = 300
ignore_number = int(iterations/100)

# Initialize objects and define references
rho_ref = qt.bell_state('00') * qt.bell_state('00').dag()
rho_ref2 = qt.bell_state('01') * qt.bell_state('01').dag()
ghz_ref = qt.ghz_state(4) * qt.ghz_state(4).dag()
ghz3_ref = qt.ghz_state(3) * qt.ghz_state(3).dag()
stab = stabilizer.Stabilizer(0, 0, 0)


cb = circuit_block.Blocks(ps, pm, pg, eta, a0, a1, theta)

# Lists to save results
FIDELITY = []
TIMES = []

# for eta in [1/30., 1/40., 1/50., 1/60., 1/70., 1/80.]:
# for a0 in [40., 30., 20., 10., 5., 2.]:
# for extra in [-20, -15, -10, -5, 0, 5, 10, 15, 20]:
# for a0 in [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0]:
for s in [0]:
    print("------> Var=", a0)
    # Get average number of steps
    fidelity = []
    times = []
    ps = []
import qutip as qt
import circuit_block
import circuit
import numpy as np

# Determine parameters
ps = 0.009
pm = 0.009
pg = 0.009
a0 = 3.
a1 = 1/80.
eta = 1/100.
theta = .24

# Initialize  objects
cb = circuit_block.Blocks(ps, pm, pg, eta, a0, a1, theta)
cb_ideal = circuit_block.Blocks(0, 0, 0, 1, 0, 0, np.pi/4)

print("------------------PROTOCOL 1-------------------")
epl = circuit.Circuit(a0=a0, a1=a1,
                      circuit_block=cb.start_epl)
rho, c = epl.run()
print("Initial F", qt.fidelity(rho, qt.bell_state('00')))
print(c)


print("------------------PROTOCOL 2-------------------")
epl = circuit.Circuit(a0=a0, a1=a1,
                      circuit_block=cb.start_epl)

circ = circuit.Circuit(a0=a0, a1=a1,