Skip to content
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

Piecemaker #159

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d8b724e
added new interactive example (no visualization yet) termed 'piecemaker'
Luisenden Sep 17, 2024
337fc92
added depolarization channel for stabilizer state repr and updated fi…
Luisenden Oct 1, 2024
39ae298
execute ghz generation only once, only entanglement generation should…
Luisenden Oct 4, 2024
54d9ad2
simple run finished, runs fusion protocol multiple times and collects…
Luisenden Oct 8, 2024
307fd09
updated README
Luisenden Oct 8, 2024
f7a3f47
adjustements in simulation setup and run script, noise applied after …
Luisenden Oct 31, 2024
8a14160
deleted init function, moved param value setting to run script
Luisenden Nov 6, 2024
0957cf6
changed GHZConsumer to FusionConsumer
Luisenden Nov 6, 2024
0aaeae1
deleted tag Piecemaker
Luisenden Nov 6, 2024
be08d75
reset project env in parent directory and added specific dependencies…
Luisenden Nov 6, 2024
81f12d1
deleted .DS_Store; updated local env
Luisenden Nov 6, 2024
cef2e06
updated readme
Luisenden Nov 6, 2024
467a314
Uptdated to event-driven implementation; added slot variables to Enta…
Luisenden Nov 21, 2024
77631f2
cleaned up code; deleted FusionSwitchDiscreteProtocol as it is not ne…
Luisenden Dec 3, 2024
e7d4349
deleted functions not in use (entangler functions and fusion function)
Luisenden Dec 3, 2024
5ab85ce
deleted .vscode
Luisenden Dec 3, 2024
46681bb
added explanation to tag FusionCounterpart
Luisenden Dec 3, 2024
badefaa
changed order of parameters in fusioncircuit to (control, target); ad…
Luisenden Dec 3, 2024
3a40643
extended function apply_noninstant! to work with depolarizing noise (…
Luisenden Dec 3, 2024
9a12dd6
updated readme
Luisenden Dec 4, 2024
751c5b1
updated readme
Luisenden Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ Manifest.toml
build
.gitignore
ROADMAP.md
coverage
coverage
.DS_Store
.vscode
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QuantumSavory"
uuid = "2de2e421-972c-4cb5-a0c3-999c85908079"
authors = ["Stefan Krastanov <[email protected]>"]
version = "0.6"
version = "0.6.0"

[deps]
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
Expand Down
11 changes: 11 additions & 0 deletions examples/piecemakerswitch/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
ConcurrentSim = "6ed1e86c-fcaf-46a9-97e0-2b26a2cdb499"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a"
QuantumSavory = "2de2e421-972c-4cb5-a0c3-999c85908079"
ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184"
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
54 changes: 54 additions & 0 deletions examples/piecemakerswitch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# System Overview
The goal is to share a GHZ state among multiple users. To do so, the clients connect to a central switch node, which then produces the GHZ state for them.

In this setup, a number of clients connect to a central hub, which we call the switch node. Each of the $n$ clients can store one memory qubit in its memory buffer and one qubit at the switch side. This makes up for $n$ memory qubits on the switch node. However, the switch contains an additional qubit, $n+1$ which we call the 'piecemaker' slot - a qubit in the $|+\rangle$ state, which is needed in the GHZ generation process.

# Entanglement Initiation
At each clock tick, the switch initiates entanglement attempts with each of the $n$ clients. So we have $n$ entanglement processes running in parallel per cycle. Successful entanglement links are immediately fused with the piecemaker qubit. Once all clients went through this fusion operation, we measure the piecemaker qubit. The latter projects the state back to the clients, resulting in the desired shared GHZ state.

# Fusion Operation
The fusion operation is performed on the switch node. Let's take a client who just managed to generate a bipartide entangled state with its associated qubit at the switch. The switch then immediately executes a `CNOT` gate on the client's qubit and the piecemaker qubit. Next, the switch measures the client qubit in the computational basis and sends the outcome to the client (in order to apply the necessary Pauli correction). This procedure merges the bipartide state into the (entangled) state that the piecemaker qubit is currently part of, modulo any required Pauli corrections.

# Noise
The memories residing in the nodes' `Register`s suffer from depolarizing noise.

### Protocol flow

```mermaid
sequenceDiagram
participant Client1
participant ClientN

participant SwitchNode
participant Log

Note over Client1,SwitchNode: Round 1 (1 unit time)
par Entanglement Generation
Client1->>+SwitchNode: Try to generate entanglement
ClientN->>+SwitchNode: Try to generate entanglement
end

SwitchNode->>SwitchNode: Run fusions with successful clients

par Send Measurement Outcomes
SwitchNode-->>-Client1: Send measurement outcomes
SwitchNode-->>-ClientN: Send measurement outcomes
end

par Apply Corrections (No time cost)
Client1->>Client1: Apply correction gates
ClientN->>ClientN: Apply correction gates
end

loop Check Fusion Status (No time cost)
SwitchNode->>SwitchNode: Check if all clients are fused
alt All clients fused
SwitchNode->>SwitchNode: Measure piecemaker
SwitchNode->>SwitchNode: Compute fidelity to GHZ state
SwitchNode->>Log: Log fidelity and time
SwitchNode->>SwitchNode: Trigger STOP
else
SwitchNode->>SwitchNode: Keep checking
end
end
```
75 changes: 75 additions & 0 deletions examples/piecemakerswitch/setup.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using QuantumSavory
using QuantumSavory.ProtocolZoo
using Graphs
using ConcurrentSim
using ResumableFunctions
using Distributions
using DataFrames
using CSV
using Profile
using NetworkLayout

@resumable function init_state(sim, net, nclients, delay)
@yield timeout(sim, delay)
initialize!(net[1][nclients+1], X1; time=now(sim))
end

@resumable function entangle_and_fuse(sim, net, client, link_success_prob)

# Set up the entanglement trackers at each client
tracker = EntanglementTracker(sim, net, client)
@process tracker()

# Set up the entangler and fuser protocols at each client
entangler = EntanglerProt(
sim=sim, net=net, nodeA=1, slotA=client-1, nodeB=client,
success_prob=link_success_prob, rounds=1, attempts=-1, attempt_time=1.0
)
@yield @process entangler()

fuser = FusionProt(
sim=sim, net=net, node=1,
nodeC=client,
rounds=1
)
@yield @process fuser()
end


@resumable function run_protocols(sim, net, nclients, link_success_prob)
# Run entangler and fusion for each client and wait for all to finish
procs_succeeded = []
for k in 2:nclients+1
proc_succeeded = @process entangle_and_fuse(sim, net, k, link_success_prob)
push!(procs_succeeded, proc_succeeded)
end
@yield reduce(&, procs_succeeded)
end

function prepare_simulation(nclients=2, mem_depolar_prob = 0.1, link_success_prob = 0.5)

m = nclients+1 # memory slots in switch is equal to the number of clients + 1 slot for piecemaker qubit
r_depol = - log(1 - mem_depolar_prob) # depolarization rate
delay = 1 # initialize the piecemaker |+> after one time unit (in order to provide fidelity ==1 if success probability = 1)

# The graph of network connectivity. Index 1 corresponds to the switch.
graph = star_graph(nclients+1)

switch_register = Register(m, Depolarization(1/r_depol)) # the first slot is reserved for the 'piecemaker' qubit used as fusion qubit
client_registers = [Register(1, Depolarization(1/r_depol)) for _ in 1:nclients] #Depolarization(1/r_depol)
net = RegisterNet(graph, [switch_register, client_registers...])
sim = get_time_tracker(net)

@process init_state(sim, net, nclients, delay)

# Run entangler and fusion for each client and wait for all to finish
@process run_protocols(sim, net, nclients, link_success_prob)

# Set up the consumer to measure final entangled state
consumer = FusionConsumer(net, net[1][m]; period=0.001)
@process consumer()

return sim, consumer
end


60 changes: 60 additions & 0 deletions examples/piecemakerswitch/simple_run.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
include("setup.jl")
using DataFrames
using CSV


# Set up the simulation parameters
name = "qs_piecemeal"

nruns = 10
mem_depolar_prob = 0.1
link_success_prob = 0.5

results_per_client = DataFrame[]
for nclients in 2:2
# Prepare simulation data storage
distribution_times = Float64[]
fidelities = Float64[]
elapsed_times = Float64[]

# Run the simulation nruns times
for i in 1:nruns
sim, consumer = prepare_simulation(nclients, mem_depolar_prob, link_success_prob)
elapsed_time = @elapsed run(sim)
# Extract data from consumer.log
distribution_time, fidelity = consumer.log[1]
append!(distribution_times, distribution_time)
append!(fidelities, fidelity)
append!(elapsed_times, elapsed_time)
@info "Run $i completed"
end

# Fill the results DataFrame
results = DataFrame(
distribution_times = distribution_times,
fidelities = fidelities,
elapsed_times = elapsed_times
)
results.num_remote_nodes .= nclients
results.link_success_prob .= link_success_prob
results.mem_depolar_prob .= mem_depolar_prob
results.type .= name

push!(results_per_client, results)
@info "Clients $nclients completed"
end
results_total = vcat(results_per_client...)

# Group and summarize the data
grouped_df = groupby(results_total, [:num_remote_nodes, :distribution_times])
summary_df = combine(
grouped_df,
:fidelities => mean => :mean_fidelities,
:fidelities => std => :std_fidelities
)

@info summary_df

# Uncomment to write results to CSV
# CSV.write("examples/piecemakerswitch/output/piecemaker-eventdriven.csv", results_total)
# CSV.write("examples/piecemakerswitch/output/piecemaker-eventdriven_summary.csv", summary_df)
15 changes: 15 additions & 0 deletions examples/piecemakerswitch/test_fusioncircuit.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using QuantumSavory
using QuantumSavory.CircuitZoo

a = Register(1)
b = Register(2)
bell = (Z₁⊗Z₁+Z₂⊗Z₂)/√2
initialize!(a[1], X1) # Initialize `a[1]` in |+⟩ state
initialize!((b[1], b[2]), bell) # Initialize `b` with a bell pair

correction = EntanglementFusion()(a[1], b[1])
isassigned(b[1])==false # the target qubit is traced out
if correction==2 apply!(b[2], X) end # apply correction if needed

# Now bell pair is fused into a
real(observable((a[1], b[2]), projector(bell)))
30 changes: 30 additions & 0 deletions examples/piecemakerswitch/test_initialize.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using QuantumSavory
using QuantumSavory.ProtocolZoo
using Graphs
using ConcurrentSim
using ResumableFunctions
using Distributions
using DataFrames
using CSV
using Profile
using NetworkLayout



@resumable function init_state(sim)
@yield timeout(sim, 1.)
initialize!(slot[1], Z1; time=now(sim))
res = observable(slot[1], projector(Z1); time=now(sim))
@info res
end

mem_depolar_prob = 0.5
r_depol = - log(1 - mem_depolar_prob)
print(r_depol)
slot = Register(1, Depolarization(1/r_depol))

sim = get_time_tracker(slot)

@process init_state(sim)
run(sim)

43 changes: 42 additions & 1 deletion src/CircuitZoo/CircuitZoo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module CircuitZoo
using QuantumSavory
using DocStringExtensions

export EntanglementSwap, LocalEntanglementSwap,
export EntanglementFusion, EntanglementSwap, LocalEntanglementSwap,
Purify2to1, Purify2to1Node, Purify3to1, Purify3to1Node,
PurifyStringent, PurifyStringentNode, PurifyExpedient, PurifyExpedientNode,
SDDecode, SDEncode
Expand Down Expand Up @@ -46,6 +46,47 @@ end

inputqubits(::LocalEntanglementSwap) = 2

""" $TYPEDEF

## Fields:

$FIELDS

A circuit that combines two multipartide entangled states (e.g., GHZ states) into one, up to some Pauli correction.
The circuit applies a CNOT gate, measures the target qubit in the Z basis and traces out the latter (removed from the register).
The measurement result (1 for |0⟩ and 2 for |1⟩) is returned by the circuit.
By measuring and discarding the target qubit, the entanglement is effectively transferred to the control qubit.

This circuit is useful in protocols where two multipartide entangled states are combined into one, e.g., when generating graph states.

```jldoctest
julia> a = Register(1)
b = Register(2)
bell = (Z₁⊗Z₁+Z₂⊗Z₂)/√2
initialize!(a[1], X1) # Initialize `a[1]` in |+⟩ state.
initialize!((b[1], b[2]), bell) # Initialize `b` with a bell pair.

julia> correction = EntanglementFusion()(a[1], b[1]) # Apply fusion and receive measurement outcome.

julia> isassigned(b[1]) # The `b[1]` qubit has been traced out.
false

julia> if correction==2 apply!(b[2], X) end # Apply correction.

julia> real(observable((a[1], b[2]), projector(bell))) # Now bell pair is fused into a.
1.0
```
"""
struct EntanglementFusion <: AbstractCircuit
end

function (::EntanglementFusion)(control, target)
apply!((control, target), CNOT)
zmeas = project_traceout!(target, σᶻ)
zmeas
end

inputqubits(::EntanglementFusion) = 2

"""
$TYPEDEF
Expand Down
Loading