Download julia/yao_kernel.jl from Snapkitty/quantum-kernel: direct link, hf CLI and curl.
- Browser
- Download file 12.1 kB
-
https://huggingface.co/Snapkitty/quantum-kernel/resolve/main/julia/yao_kernel.jl
- Command line
-
hf download hf://Snapkitty/quantum-kernel/julia/yao_kernel.jl
-
curl -L -o yao_kernel.jl https://huggingface.co/Snapkitty/quantum-kernel/resolve/main/julia/yao_kernel.jl
12.1 kB
| # yao_kernel.jl | |
| # | |
| # Complete Yao.jl circuit construction for Quantum Kernel Architecture: | |
| # Feature Map U_Φ(x) + Inverse U_Φ(x')† + DFE Measurement + Classical Feedforward | |
| include("yao_types.jl") | |
| include("yao_circuit.jl") | |
| include("yao_to_ir.jl") | |
| using LinearAlgebra | |
| using Random | |
| # ----------------------------------------------------------------------- | |
| # Heron Topology & Qubit Mapping | |
| # ----------------------------------------------------------------------- | |
| """ | |
| HERON_HEAVY_HEX_EDGES | |
| Heavy-hex connectivity for 10-qubit subset (from diagram): | |
| q0-q1-q2 | |
| |/|/|/| | |
| q3 q4 q5 q6 | |
| |\\|\\|\\| | |
| q7-q8-q9 | |
| """ | |
| const HERON_HEAVY_HEX_EDGES = [ | |
| (1,2), (2,3), | |
| (1,4), (2,4), (2,5), (3,5), (3,6), | |
| (4,5), (5,6), (6,7), | |
| (4,8), (5,8), (5,9), (6,9), (6,10), | |
| (8,9), (9,10) | |
| ] | |
| const HERON_EDGES_0 = [(a-1, b-1) for (a,b) in HERON_HEAVY_HEX_EDGES] | |
| # ----------------------------------------------------------------------- | |
| # Feature Map Parameters | |
| # ----------------------------------------------------------------------- | |
| """ | |
| FeatureMapParams | |
| Trainable parameters θ for feature map. | |
| Shape: (n_layers, n_qubits, 3) for [θz1, θy, θz2] per qubit per layer. | |
| """ | |
| struct FeatureMapParams | |
| data::Array{Float64,3} | |
| end | |
| function FeatureMapParams(n_layers::Int, n_qubits::Int; init_scale::Float64=0.1) | |
| data = randn(n_layers, n_qubits, 3) * init_scale .+ 1.0 | |
| return FeatureMapParams(data) | |
| end | |
| Base.getindex(p::FeatureMapParams, layer::Int, qubit::Int, param::Int) = p.data[layer, qubit, param] | |
| Base.setindex!(p::FeatureMapParams, val, layer::Int, qubit::Int, param::Int) = p.data[layer, qubit, param] = val | |
| # ----------------------------------------------------------------------- | |
| # Single-Qubit Data Encoding Block | |
| # ----------------------------------------------------------------------- | |
| """ | |
| data_encoding_block(qubit::Int, x::Float64, θz1::Float64, θy::Float64, θz2::Float64) | |
| R_Z(2xθz1) · R_Y(2xθy) · R_Z(2xθz2) on a single qubit. | |
| """ | |
| function data_encoding_block(qubit::Int, x::Float64, θz1::Float64, θy::Float64, θz2::Float64) | |
| return chain(1, | |
| put(1, [1], Rz(2x * θz1)), | |
| put(1, [1], Ry(2x * θy)), | |
| put(1, [1], Rz(2x * θz2)) | |
| ) | |
| end | |
| # ----------------------------------------------------------------------- | |
| # Feature Map U_Φ(x) Construction | |
| # ----------------------------------------------------------------------- | |
| """ | |
| build_feature_map(n_qubits, n_layers, features, params, ent_edges) | |
| Build U_Φ(x) = ∏_l [U_ent · U_rot(x)] as a Yao.jl ChainBlock. | |
| """ | |
| function build_feature_map(n_qubits::Int, n_layers::Int, | |
| features::Vector{Float64}, | |
| params::FeatureMapParams, | |
| ent_edges::Vector{Tuple{Int,Int}}=HERON_EDGES_0)::ChainBlock | |
| layers = AbstractBlock[] | |
| for layer in 0:n_layers-1 | |
| # Parallel single-qubit data encoding (KronBlock = true parallelism) | |
| encoding_blocks = Pair{Int,AbstractBlock}[] | |
| for q in 0:n_qubits-1 | |
| x = features[(q % length(features)) + 1] | |
| θz1 = params[layer+1, q+1, 1] | |
| θy = params[layer+1, q+1, 2] | |
| θz2 = params[layer+1, q+1, 3] | |
| enc_block = data_encoding_block(1, x, θz1, θy, θz2) | |
| push!(encoding_blocks, q+1 => enc_block) | |
| end | |
| push!(layers, kron(n_qubits, encoding_blocks...)) | |
| # Entangling layer on heavy-hex edges (sequential) | |
| ent_blocks = AbstractBlock[] | |
| for (q1, q2) in ent_edges | |
| if q1 < n_qubits && q2 < n_qubits | |
| cz_block = control(n_qubits, [q1+1], q2+1 => Z()) | |
| push!(ent_blocks, cz_block) | |
| end | |
| end | |
| if !isempty(ent_blocks) | |
| push!(layers, chain(n_qubits, ent_blocks...)) | |
| end | |
| end | |
| return chain(n_qubits, layers...) | |
| end | |
| # ----------------------------------------------------------------------- | |
| # Inverse Feature Map U_Φ(x)† | |
| # ----------------------------------------------------------------------- | |
| """ | |
| build_inverse_feature_map(n_qubits, n_layers, features, params, ent_edges) | |
| Build U_Φ(x)† = ∏_l [U_ent† · U_rot(x)†] with reversed layer order and negative angles. | |
| """ | |
| function build_inverse_feature_map(n_qubits::Int, n_layers::Int, | |
| features::Vector{Float64}, | |
| params::FeatureMapParams, | |
| ent_edges::Vector{Tuple{Int,Int}}=HERON_EDGES_0)::ChainBlock | |
| layers = AbstractBlock[] | |
| for layer in n_layers-1:-1:0 | |
| # Entangling layer (CZ is self-adjoint) | |
| ent_blocks = AbstractBlock[] | |
| for (q1, q2) in ent_edges | |
| if q1 < n_qubits && q2 < n_qubits | |
| cz_block = control(n_qubits, [q1+1], q2+1 => Z()) | |
| push!(ent_blocks, cz_block) | |
| end | |
| end | |
| if !isempty(ent_blocks) | |
| push!(layers, chain(n_qubits, ent_blocks...)) | |
| end | |
| # Single-qubit adjoint: reverse order, negative angles | |
| encoding_blocks = Pair{Int,AbstractBlock}[] | |
| for q in n_qubits-1:-1:0 | |
| x = features[(q % length(features)) + 1] | |
| θz1 = params[layer+1, q+1, 1] | |
| θy = params[layer+1, q+1, 2] | |
| θz2 = params[layer+1, q+1, 3] | |
| # Adjoint: RZ(-2xθz2) · RY(-2xθy) · RZ(-2xθz1) | |
| enc_block = chain(1, | |
| put(1, [1], Rz(-2x * θz2)), | |
| put(1, [1], Ry(-2x * θy)), | |
| put(1, [1], Rz(-2x * θz1)) | |
| ) | |
| push!(encoding_blocks, q+1 => enc_block) | |
| end | |
| push!(layers, kron(n_qubits, encoding_blocks...)) | |
| end | |
| return chain(n_qubits, layers...) | |
| end | |
| # ----------------------------------------------------------------------- | |
| # DFE Measurement Circuit | |
| # ----------------------------------------------------------------------- | |
| """ | |
| build_dfe_measurement(n_qubits, pauli_basis) | |
| Build mid-circuit measurement in Pauli basis with conditional reset. | |
| """ | |
| function build_dfe_measurement(n_qubits::Int, pauli_basis::Vector{Char})::ChainBlock | |
| blocks = AbstractBlock[] | |
| # Pauli basis rotation | |
| rotation_blocks = Pair{Int,AbstractBlock}[] | |
| for (q, pauli) in enumerate(pauli_basis) | |
| if pauli == 'X' | |
| push!(rotation_blocks, q => chain(1, put(1, [1], H()))) | |
| elseif pauli == 'Y' | |
| push!(rotation_blocks, q => chain(1, put(1, [1], Sdg()), put(1, [1], H()))) | |
| end | |
| end | |
| if !isempty(rotation_blocks) | |
| push!(blocks, kron(n_qubits, rotation_blocks...)) | |
| end | |
| # Mid-circuit measurement | |
| meas_locs = collect(1:n_qubits) | |
| push!(blocks, measure(n_qubits, meas_locs)) | |
| # Conditional reset is handled in QASM emission (classical feedforward) | |
| # Yao.jl doesn't directly support classical feedforward in blocks | |
| return chain(n_qubits, blocks...) | |
| end | |
| # ----------------------------------------------------------------------- | |
| # Full DFE Kernel Circuit | |
| # ----------------------------------------------------------------------- | |
| """ | |
| build_dfe_kernel_circuit(n_qubits, n_layers, features_a, features_b, params, pauli_basis, ent_edges) | |
| Build complete DFE kernel circuit for one shot: | |
| U_Φ(x) · U_Φ(x')† · Pauli_rotation · Measure | |
| """ | |
| function build_dfe_kernel_circuit(n_qubits::Int, n_layers::Int, | |
| features_a::Vector{Float64}, | |
| features_b::Vector{Float64}, | |
| params::FeatureMapParams, | |
| pauli_basis::Vector{Char}, | |
| ent_edges::Vector{Tuple{Int,Int}}=HERON_EDGES_0)::ChainBlock | |
| blocks = AbstractBlock[] | |
| # U_Φ(x) | |
| push!(blocks, build_feature_map(n_qubits, n_layers, features_a, params, ent_edges)) | |
| # U_Φ(x')† | |
| push!(blocks, build_inverse_feature_map(n_qubits, n_layers, features_b, params, ent_edges)) | |
| # Pauli basis rotation + measurement | |
| push!(blocks, build_dfe_measurement(n_qubits, pauli_basis)) | |
| return chain(n_qubits, blocks...) | |
| end | |
| # ----------------------------------------------------------------------- | |
| # Batch Kernel Matrix Circuit Generation | |
| # ----------------------------------------------------------------------- | |
| """ | |
| generate_kernel_circuits(dataset, params, n_layers, shots_per_entry, anu_bases) | |
| Generate Yao circuits for all kernel matrix entries with ANU QRNG bases. | |
| Returns Dict mapping (i,j) -> Vector{ChainBlock} (one per shot). | |
| """ | |
| function generate_kernel_circuits(dataset::Vector{Vector{Float64}}, | |
| params::FeatureMapParams, | |
| n_layers::Int, | |
| shots_per_entry::Int, | |
| anu_bases::Vector{Vector{Char}}; | |
| ent_edges::Vector{Tuple{Int,Int}}=HERON_EDGES_0) | |
| n_samples = length(dataset) | |
| n_qubits = size(params.data, 2) | |
| circuits = Dict{Tuple{Int,Int}, Vector{ChainBlock}}() | |
| for i in 1:n_samples | |
| for j in i:n_samples | |
| shot_circuits = ChainBlock[] | |
| for shot in 1:shots_per_entry | |
| basis_idx = (i-1)*n_samples + (j-1) | |
| basis_idx = (basis_idx * shots_per_entry + shot - 1) % length(anu_bases) + 1 | |
| basis = anu_bases[basis_idx] | |
| circuit = build_dfe_kernel_circuit( | |
| n_qubits, n_layers, dataset[i], dataset[j], params, basis, ent_edges | |
| ) | |
| push!(shot_circuits, circuit) | |
| end | |
| circuits[(i,j)] = shot_circuits | |
| end | |
| end | |
| return circuits | |
| end | |
| # ----------------------------------------------------------------------- | |
| # Lower All Circuits to QuantumIR | |
| # ----------------------------------------------------------------------- | |
| """ | |
| lower_kernel_to_ir(circuits) -> Vector{Dict} | |
| Lower all kernel circuits to QuantumIR JSON format. | |
| """ | |
| function lower_kernel_to_ir(circuits::Dict{Tuple{Int,Int}, Vector{ChainBlock}}) | |
| ir_list = Dict{String,Any}[] | |
| for ((i,j), shot_circuits) in circuits | |
| for (shot, circuit) in enumerate(shot_circuits) | |
| ir = yao_to_ir(circuit) | |
| ir["metadata"]["kernel_entry"] = [i, j] | |
| ir["metadata"]["shot"] = shot | |
| push!(ir_list, ir) | |
| end | |
| end | |
| return ir_list | |
| end | |
| # ----------------------------------------------------------------------- | |
| # Example: Generate Kernel for Circles Dataset | |
| # ----------------------------------------------------------------------- | |
| function generate_circles_dataset(n::Int; noise::Float64=0.1) | |
| X = Vector{Vector{Float64}}(undef, n) | |
| y = Vector{Float64}(undef, n) | |
| for i in 1:n | |
| r = rand() | |
| θ = rand() * 2π | |
| if i ≤ n÷2 | |
| r = 0.5 + r * 0.3 | |
| y[i] = -1.0 | |
| else | |
| r = 1.0 + r * 0.3 | |
| y[i] = 1.0 | |
| end | |
| X[i] = [r * cos(θ), r * sin(θ)] | |
| X[i] .+= randn(2) * noise | |
| end | |
| return X, y | |
| end | |
| function demo_kernel_generation() | |
| # Dataset | |
| X, y = generate_circles_dataset(20, noise=0.1) | |
| # Parameters | |
| n_qubits = 4 | |
| n_layers = 2 | |
| shots = 100 | |
| params = FeatureMapParams(n_layers, n_qubits) | |
| # ANU QRNG bases (simulated for demo) | |
| anu_bases = [rand(['I','X','Y','Z'], n_qubits) for _ in 1:10000] | |
| # Generate circuits | |
| circuits = generate_kernel_circuits(X, params, n_layers, shots, anu_bases) | |
| # Lower to QuantumIR | |
| ir_list = lower_kernel_to_ir(circuits) | |
| # Save | |
| open("kernel_ir.json", "w") do f | |
| JSON3.write(f, ir_list) | |
| end | |
| println("Generated $(length(ir_list)) QuantumIR circuits") | |
| println("Saved to kernel_ir.json") | |
| # Convert first circuit to OpenQASM 3.0 | |
| first_ir = ir_list[1] | |
| qasm = qir_to_openqasm3(first_ir; zne_factors=[1.0, 1.5, 2.0, 3.0], | |
| anu_bases=anu_bases[1:shots], | |
| dynamic_shots=true) | |
| write("kernel.qasm3", qasm) | |
| println("Written kernel.qasm3") | |
| end | |
| if abspath(PROGRAM_FILE) == | |
| demo_kernel_generation() | |
| end | |