From 11ea5e45554a705e72a7a44dddb4f545938d5ec0 Mon Sep 17 00:00:00 2001 From: Feroz Ahmad Date: Wed, 9 Oct 2024 21:25:21 +0500 Subject: [PATCH 1/4] feature: Expanded support for quantum chemistry operations (#25) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Feature]: Expanded support for quantum chemistry operations * [Feature]: Expanded support for quantum chemistry operations: Improved tests for SingleExcitationPlus and SingleExcitationMinus * [Feature]: Expanded support for quantum chemistry operations: Formatting * [Feature]: Expanded support for quantum chemistry operations: Tests for DoubleExcitationPlus, DoubleExcitationMinus, FermionicSWAP * [Feature]: Expanded support for quantum chemistry operations: Completing docstrings for all quantum chemistry gates * [Feature]: Expanded support for quantum chemistry operations: Adding hrefs for qchem gates and minor cleanup * [Feature]: Expanded support for quantum chemistry operations - fix documentation * [Feature]: Expanded support for quantum chemistry operations - codereview * [Feature]: Expanded support for quantum chemistry operations - fix doublexcitationplus and doubleexcitation minus * code review suggestions * Minor Formating changes, Polishing up everything * Formating changes * using ϕ instead of \varphi * adding gate exponentiation and tests * Polishing up gate exponentiation, and testing it * using iszero check for gate exponentiation power == 0 case * Polishing up * Fix CI Doc error for quantum chemistry gates * restoring LaTeX rendering * polishing up --------- Co-authored-by: Fe-r-oz --- src/custom_gates.jl | 332 ++++++++++++++++++++++++++++++++++++++ test/test_custom_gates.jl | 154 ++++++++++++++++++ 2 files changed, 486 insertions(+) diff --git a/src/custom_gates.jl b/src/custom_gates.jl index aa102bd..87d84b6 100644 --- a/src/custom_gates.jl +++ b/src/custom_gates.jl @@ -1,3 +1,36 @@ +""" + DoubleExcitation(ϕ) + +Generate the matrix representation of the [DoubleExcitation](https://docs.pennylane.ai/en/stable/code/api/pennylane.DoubleExcitation.html) gate. + +This gate performs an SO(2) rotation in the subspace ``{|1100\\rangle, |0011\\rangle}``, transforming the states as follows: + +```math +|0011\\rangle & \\rightarrow \\cos\\left(\\frac{\\phi}{2}\\right)|0011\\rangle + \\sin\\left(\\frac{\\phi}{2}\\right)|1100\\rangle \\ +|1100\\rangle & \\rightarrow \\cos\\left(\\frac{\\phi}{2}\\right)|1100\\rangle - \\sin\\left(\\frac{\\phi}{2}\\right)|0011\\rangle +``` + +# Examples + +```jldoctest +julia> using BraketSimulator + +julia> ϕ = 3.56; + +julia> gate_matrix = BraketSimulator.DoubleExcitation(ϕ); + +julia> m = BraketSimulator.matrix_rep(gate_matrix); + +julia> eq1 = m * [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + +julia> eq2 = m * [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]; + +julia> eq1 == [0, 0, 0, cos(ϕ/2), 0, 0, 0, 0, 0, 0, 0, 0, sin(ϕ/2), 0, 0, 0] == true; + +julia> eq2 == [0, 0, 0, -sin(ϕ/2), 0, 0, 0, 0, 0, 0, 0, 0, cos(ϕ/2), 0, 0, 0] == true; +``` + +""" mutable struct DoubleExcitation <: AngledGate{1} angle::NTuple{1,Union{Real,FreeParameter}} pow_exponent::Float64 @@ -15,6 +48,155 @@ function matrix_rep_raw(::DoubleExcitation, ϕ) # nosemgrep return SMatrix{16,16,ComplexF64}(mat) end +""" + DoubleExcitationPlus(ϕ) + +Generate the matrix representation of the [DoubleExcitationPlus](https://docs.pennylane.ai/en/stable/code/api/pennylane.DoubleExcitationPlus.html) gate. + +This gate performs an SO(2) rotation in the subspace ``{|1100\\rangle, |0011\\rangle}`` with a phase-shift on other states: + +```math +|0011\\rangle & \\rightarrow \\cos\\left(\\frac{\\phi}{2}\\right)|0011\\rangle - \\sin\\left(\\frac{\\phi}{2}\\right)|1100\\rangle \\ +|1100\\rangle & \\rightarrow \\cos\\left(\\frac{\\phi}{2}\\right)|1100\\rangle + \\sin\\left(\\frac{\\phi}{2}\\right)|0011\\rangle \\ +|x\\rangle & \\rightarrow e^{\\frac{i\\phi}{2}}|x\\rangle \\quad \\text{for all other basis states } |x\\rangle +``` + +# Examples + +```jldoctest +julia> using BraketSimulator + +julia> ϕ = 3.56; + +julia> gate_matrix = BraketSimulator.DoubleExcitationPlus(ϕ); + +julia> m = BraketSimulator.matrix_rep(gate_matrix); + +julia> eq1 = m * [1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; + +julia> eq2 = m * [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1]; + +julia> eq1 == [exp(im*ϕ/2), 0, 0, cos(ϕ/2), 0, 0, 0, 0, 0, 0, 0, 0, sin(ϕ/2), 0, 0, exp(im*ϕ/2)] == true; + +julia> eq2 == [exp(im*ϕ/2), 0, 0, -sin(ϕ/2), 0, 0, 0, 0, 0, 0, 0, 0, cos(ϕ/2), 0, 0, exp(im*ϕ/2)] == true; +``` + +""" +struct DoubleExcitationPlus <: AngledGate{1} + angle::NTuple{1,Union{Float64,FreeParameter}} + DoubleExcitationPlus(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = + new(angle) +end +Braket.chars(::Type{DoubleExcitationPlus}) = "G2+(ang)" +Braket.qubit_count(::Type{DoubleExcitationPlus}) = 4 +Base.inv(g::DoubleExcitationPlus) = DoubleExcitationPlus(-g.angle[1]) +Base.:^(g::DoubleExcitationPlus, power::Integer) = power == -1 ? inv(g) : (iszero(power) ? Braket.I() : (power < 0 ? inv(g^(-power)) : DoubleExcitationPlus((g.angle[1] * power,)))) +function matrix_rep(g::DoubleExcitationPlus) + cosϕ = cos(g.angle[1] / 2.0) + sinϕ = sin(g.angle[1] / 2.0) + eiϕ2 = exp(im * g.angle[1] / 2.0) + mat = diagm(eiϕ2 * ones(ComplexF64, 16)) + + mat[4, :] .= 0 + mat[:, 4] .= 0 + mat[13, :] .= 0 + mat[:, 13] .= 0 + # Apply phase-shift to states outside rotation subspace + mat[4, 4] = cosϕ + mat[13, 13] = cosϕ + mat[4, 13] = -sinϕ + mat[13, 4] = sinϕ + return SMatrix{16, 16, ComplexF64}(mat) +end +""" + DoubleExcitationMinus(ϕ) + +Generate the matrix representation of the [DoubleExcitationMinus](https://docs.pennylane.ai/en/stable/code/api/pennylane.DoubleExcitationMinus.html) gate. + +This gate performs an SO(2) rotation in the subspace ``{|1100\\rangle, |0011\\rangle}`` with a phase-shift on other states: + +```math +|0011\\rangle & \\rightarrow \\cos\\left(\\frac{\\phi}{2}\\right)|0011\\rangle - \\sin\\left(\\frac{\\phi}{2}\\right)|1100\\rangle \\ +|1100\\rangle & \\rightarrow \\cos\\left(\\frac{\\phi}{2}\\right)|1100\\rangle + \\sin\\left(\\frac{\\phi}{2}\\right)|0011\\rangle \\ +|x\\rangle & \\rightarrow e^{-\\frac{i\\phi}{2}}|x\\rangle \\quad \\text{for all other basis states } |x\\rangle +``` + +# Examples + +```jldoctest +julia> using BraketSimulator + +julia> ϕ = 3.56; + +julia> gate_matrix = BraketSimulator.DoubleExcitationMinus(ϕ); + +julia> m = BraketSimulator.matrix_rep(gate_matrix); + +julia> eq1 = m * [1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; + +julia> eq2 = m * [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1]; + +julia> eq1 == [exp(-im*ϕ/2), 0, 0, cos(ϕ/2), 0, 0, 0, 0, 0, 0, 0, 0, sin(ϕ/2), 0, 0, exp(-im*ϕ/2)] == true; + +julia> eq2 == [exp(-im*ϕ/2), 0, 0, -sin(ϕ/2), 0, 0, 0, 0, 0, 0, 0, 0, cos(ϕ/2), 0, 0, exp(-im*ϕ/2)] == true; +``` + +""" +struct DoubleExcitationMinus <: AngledGate{1} + angle::NTuple{1,Union{Float64,FreeParameter}} + DoubleExcitationMinus(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = + new(angle) +end +Braket.chars(::Type{DoubleExcitationMinus}) = "G2-(ang)" +Braket.qubit_count(::Type{DoubleExcitationMinus}) = 4 +Base.inv(g::DoubleExcitationMinus) = DoubleExcitationMinus(-g.angle[1]) +Base.:^(g::DoubleExcitationMinus, power::Integer) = power == -1 ? inv(g) : (iszero(power) ? Braket.I() : (power < 0 ? inv(g^(-power)) : DoubleExcitationMinus((g.angle[1] * power,)))) +function matrix_rep(g::DoubleExcitationMinus) + cosϕ = cos(g.angle[1] / 2.0) + sinϕ = sin(g.angle[1] / 2.0) + eiϕ2 = exp(-im * g.angle[1] / 2.0) + mat = diagm(eiϕ2 * ones(ComplexF64, 16)) + + mat[4, :] .= 0 + mat[:, 4] .= 0 + mat[13, :] .= 0 + mat[:, 13] .= 0 + # Apply phase-shift to states outside rotation subspace + mat[4, 4] = cosϕ + mat[13, 13] = cosϕ + mat[4, 13] = -sinϕ + mat[13, 4] = sinϕ + return SMatrix{16, 16, ComplexF64}(mat) +end + +""" + SingleExcitation(ϕ) + +Generate the matrix representation of the [SingleExcitation](https://docs.pennylane.ai/en/stable/code/api/pennylane.SingleExcitation.html) gate. + +This gate performs a rotation in the subspace ``{|01\\rangle, |10\\rangle}``. + +# Examples + +```jldoctest +julia> using BraketSimulator + +julia> ϕ = 3.56; + +julia> gate_matrix = BraketSimulator.SingleExcitation(ϕ); + +julia> m = BraketSimulator.matrix_rep(gate_matrix); + +julia> eq1 = m * [0, 1, 0, 0]; + +julia> eq2 = m * [0, 0, 1, 0]; + +julia> eq1 == [0, cos(ϕ/2), - sin(ϕ/2), 0] == true; + +julia> eq2 == [0, sin(ϕ/2), cos(ϕ/2), 0] == true; +``` + +""" mutable struct SingleExcitation <: AngledGate{1} angle::NTuple{1,Union{Real,FreeParameter}} pow_exponent::Float64 @@ -23,6 +205,156 @@ mutable struct SingleExcitation <: AngledGate{1} end qubit_count(::Type{SingleExcitation}) = 2 matrix_rep_raw(::SingleExcitation, ϕ) = ((sθ, cθ) = sincos(ϕ/2.0); return SMatrix{4,4,ComplexF64}(complex(1.0), 0, 0, 0, 0, cθ, -sθ, 0, 0, sθ, cθ, 0, 0, 0, 0, complex(1.0))) +""" + SingleExcitationPlus(ϕ) + +Generate the matrix representation of the [SingleExcitationPlus](https://docs.pennylane.ai/en/stable/code/api/pennylane.SingleExcitationPlus.html) gate. + +This gate performs a rotation in the subspace ``{|01\\rangle, |10\\rangle}`` with a phase-shift. + +# Examples + +```jldoctest +julia> using BraketSimulator + +julia> ϕ = 3.56; + +julia> gate_matrix = BraketSimulator.SingleExcitationPlus(ϕ); + +julia> m = BraketSimulator.matrix_rep(gate_matrix); + +julia> eq1 = m * [1, 1, 0, 0]; + +julia> eq2 = m * [1, 0, 1, 0]; + +julia> eq1 == [exp(im*ϕ/2), cos(ϕ/2), - sin(ϕ/2), 0] == true; + +julia> eq2 == [exp(im*ϕ/2), sin(ϕ/2), cos(ϕ/2), 0] == true; +``` + +""" +struct SingleExcitationPlus <: AngledGate{1} + angle::NTuple{1,Union{Float64,FreeParameter}} + SingleExcitationPlus(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = + new(angle) +end +Braket.chars(::Type{SingleExcitationPlus}) = "G+(ang)" +Braket.qubit_count(::Type{SingleExcitationPlus}) = 2 +Base.inv(g::SingleExcitationPlus) = SingleExcitationPlus(-g.angle[1]) +Base.:^(g::SingleExcitationPlus, power::Integer) = power == -1 ? inv(g) : (iszero(power) ? Braket.I() : (power < 0 ? inv(g^(-power)) : SingleExcitationPlus((g.angle[1] * power,)))) +function matrix_rep(g::SingleExcitationPlus) + cosϕ = cos(g.angle[1] / 2.0) + sinϕ = sin(g.angle[1] / 2.0) + eiϕ2 = exp(im * g.angle[1] / 2.0) + return SMatrix{4,4,ComplexF64}([eiϕ2 0 0 0; 0 cosϕ sinϕ 0; 0 -sinϕ cosϕ 0; 0 0 0 eiϕ2]) +end + +""" + SingleExcitationMinus(ϕ) + +Generate the matrix representation of the [SingleExcitationMinus](https://docs.pennylane.ai/en/stable/code/api/pennylane.SingleExcitationMinus.html) gate. + +This gate performs a rotation in the subspace ``{|01\\rangle, |10\\rangle}`` with a phase-shift. + +# Examples + +```jldoctest +julia> using BraketSimulator + +julia> ϕ = 3.56; + +julia> gate_matrix = BraketSimulator.SingleExcitationMinus(ϕ); + +julia> m = BraketSimulator.matrix_rep(gate_matrix); + +julia> eq1 = m * [1, 1, 0, 0]; + +julia> eq2 = m * [1, 0, 1, 0]; + +julia> eq1 == [exp(-im*ϕ/2), cos(ϕ/2), - sin(ϕ/2), 0] == true; + +julia> eq2 == [exp(-im*ϕ/2), sin(ϕ/2), cos(ϕ/2), 0] == true; +``` + +""" +struct SingleExcitationMinus <: AngledGate{1} + angle::NTuple{1,Union{Float64,FreeParameter}} + SingleExcitationMinus(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = + new(angle) +end +Braket.chars(::Type{SingleExcitationMinus}) = "G-(ang)" +Braket.qubit_count(::Type{SingleExcitationMinus}) = 2 +Base.inv(g::SingleExcitationMinus) = SingleExcitationMinus(-g.angle[1]) +Base.:^(g::SingleExcitationMinus, power::Integer) = power == -1 ? inv(g) : (iszero(power) ? Braket.I() : (power < 0 ? inv(g^(-power)) : SingleExcitationMinus((g.angle[1] * power,)))) +function matrix_rep(g::SingleExcitationMinus) + cosϕ = cos(g.angle[1] / 2.0) + sinϕ = sin(g.angle[1] / 2.0) + eiϕ2 = exp(-im * g.angle[1] / 2.0) + return SMatrix{4,4,ComplexF64}([eiϕ2 0 0 0; 0 cosϕ sinϕ 0; 0 -sinϕ cosϕ 0; 0 0 0 eiϕ2]) +end + +""" + FermionicSWAP(ϕ) + +Generate the matrix representation of the [FermionicSWAP](https://docs.pennylane.ai/en/stable/code/api/pennylane.FermionicSWAP.html) gate. + +This gate performs a rotation in adjacent fermionic modes under the Jordan-Wigner mapping, transforming states as follows: + +```math +|00\\rangle & \\rightarrow |00\\rangle \\ +|01\\rangle & \\rightarrow e^{\\frac{i\\phi}{2}}\\cos\\left(\\frac{\\phi}{2}\\right)|01\\rangle - ie^{\\frac{i\\phi}{2}}\\sin\\left(\\frac{\\phi}{2}\\right)|10\\rangle \\ +|10\\rangle & \\rightarrow -ie^{\\frac{i\\phi}{2}}\\sin\\left(\\frac{\\phi}{2}\\right)|01\\rangle + e^{\\frac{i\\phi}{2}}\\cos\\left(\\frac{\\phi}{2}\\right)|10\\rangle \\ +|11\\rangle & \\rightarrow e^{i\\phi}|11\\rangle +``` + +# Examples + +```jldoctest +julia> using BraketSimulator + +julia> ϕ = 3.56; + +julia> gate_matrix = BraketSimulator.FermionicSWAP(ϕ); + +julia> m = BraketSimulator.matrix_rep(gate_matrix); + +julia> eq1 = m * [0, 0, 0, 0]; + +julia> eq2 = m * [0, 1, 0, 0]; + +julia> eq3 = m * [0, 0, 1, 0]; + +julia> eq4 = m * [0, 0, 0, 1]; + +julia> eq1 == [0, 0, 0, 0] == true; + +julia> eq2 == [0, exp(im*ϕ/2.0)*cos(ϕ / 2.0), - im*exp(im*ϕ/2.0)*sin(ϕ/2.0), 0] == true; + +julia> eq3 == [0, - im*exp(im*ϕ/2.0)*sin(ϕ/2.0), exp(im*ϕ/2.0)*cos(ϕ/2.0), 0] == true; + +julia> eq4 == [0, 0, 0, exp(im * ϕ)] == true; +``` + +""" +struct FermionicSWAP <: AngledGate{1} + angle::NTuple{1,Union{Float64,FreeParameter}} + FermionicSWAP(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = + new(angle) +end +Braket.chars(::Type{FermionicSWAP}) = "FSWAP(ang)" +Braket.qubit_count(::Type{FermionicSWAP}) = 2 +Base.inv(g::FermionicSWAP) = FermionicSWAP(-g.angle[1]) +Base.:^(g::FermionicSWAP, power::Integer) = power == -1 ? inv(g) : (power == 0 ? Braket.I() : (power < 0 ? inv(g^(-power)) : FermionicSWAP((g.angle[1] * power,)))) +function matrix_rep(g::FermionicSWAP) + cosϕ = cos(g.angle[1] / 2.0) + sinϕ = sin(g.angle[1] / 2.0) + eiϕ2 = exp(im * g.angle[1] / 2.0) + eiϕ = exp(im * g.angle[1]) + ieiϕ2 = im * eiϕ2 + + return SMatrix{4,4,ComplexF64}([1.0 0 0 0; 0 eiϕ2 * cosϕ -ieiϕ2 * sinϕ 0; 0 -ieiϕ2 * sinϕ eiϕ2 * cosϕ 0; 0 0 0 eiϕ]) +end + """ MultiRz(angle) diff --git a/test/test_custom_gates.jl b/test/test_custom_gates.jl index 7163d48..d62ac1b 100644 --- a/test/test_custom_gates.jl +++ b/test/test_custom_gates.jl @@ -26,9 +26,17 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end + + @test qubit_count(DoubleExcitation(ϕ)) == 4 + @test inv(DoubleExcitation(ϕ)) == DoubleExcitation(-ϕ) + @test DoubleExcitation(ϕ) ^ 0 == Braket.I() + @test DoubleExcitation(ϕ) ^ 2 == DoubleExcitation(2*ϕ) + @test DoubleExcitation(ϕ) ^ -1 == inv(DoubleExcitation(ϕ)) + @test DoubleExcitation(ϕ) ^ - 3 == inv(DoubleExcitation(3*ϕ)) @test BraketSimulator.qubit_count(BraketSimulator.DoubleExcitation(ϕ)) == 4 @test BraketSimulator.qubit_count(BraketSimulator.DoubleExcitation) == 4 @test inv(BraketSimulator.DoubleExcitation(ϕ)) == BraketSimulator.DoubleExcitation(ϕ, -1.0) + end @testset "Single excitation" begin ϕ = 3.56 @@ -54,9 +62,16 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end + @test qubit_count(SingleExcitation(ϕ)) == 2 + @test inv(SingleExcitation(ϕ)) == SingleExcitation(-ϕ) + @test SingleExcitation(ϕ) ^ 0 == Braket.I() + @test SingleExcitation(ϕ) ^ 2 == SingleExcitation(2*ϕ) + @test SingleExcitation(ϕ) ^ -1 == inv(SingleExcitation(ϕ)) + @test SingleExcitation(ϕ) ^ - 3 == inv(SingleExcitation(3*ϕ)) @test BraketSimulator.qubit_count(BraketSimulator.SingleExcitation(ϕ)) == 2 @test BraketSimulator.qubit_count(BraketSimulator.SingleExcitation) == 2 @test inv(BraketSimulator.SingleExcitation(ϕ)) == BraketSimulator.SingleExcitation(ϕ, -1.0) + end @testset "3-angle U" begin θ = 1.34 @@ -176,4 +191,143 @@ using Test, Logging, BraketSimulator, DataStructures end end end + + @testset "Single excitation plus" begin + ϕ = 3.56 + nq = 2 + instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(SingleExcitationPlus(ϕ), [0, 1])] + # instructions for the gate decomposition (from PennyLane) + de_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(X(), [0]), Instruction(X(), [1]), Instruction(CPhaseShift(ϕ/2), [1, 0]), Instruction(X(), [0]), Instruction(X(), [1]), Instruction(CPhaseShift(ϕ/2), [0, 1]), Instruction(CNot(), [0, 1]), Instruction(Ry(ϕ/2), [0]), Instruction(CNot(), [1, 0]), Instruction(Ry(-ϕ/2), [0]), Instruction(CNot(), [1, 0]), Instruction(CNot(), [0, 1])] + # instructions for the matrix representation + u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(SingleExcitationPlus(ϕ)))), [1, 0])] + # state vector for SingleExcitationPlus (from PennyLane) + state_vector = 0.5 * [exp(im*ϕ/2), cos(ϕ/2) - sin(ϕ/2), cos(ϕ/2) + sin(ϕ/2), exp(im*ϕ/2)] + probability_amplitudes = 0.25 * [1, (cos(ϕ/2) - sin(ϕ/2))^2, (cos(ϕ/2) + sin(ϕ/2))^2, 1] + @testset "Simulator $sim, instruction set $ix_label" for sim in (StateVectorSimulator, DensityMatrixSimulator), + (ix_label, ixs) in (("raw", instructions), ("decomp", de_instructions), ("unitary", u_instructions)) + simulation = sim(nq, 0) + simulation = evolve!(simulation, ixs) + if sim == StateVectorSimulator + @test state_vector ≈ collect(BraketSimulator.state_vector(simulation)) + end + @test probability_amplitudes ≈ + collect(BraketSimulator.probabilities(simulation)) + end + @test qubit_count(SingleExcitationPlus(ϕ)) == 2 + @test inv(SingleExcitationPlus(ϕ)) == SingleExcitationPlus(-ϕ) + @test SingleExcitationPlus(ϕ) ^ 0 == Braket.I() + @test SingleExcitationPlus(ϕ) ^ 2 == SingleExcitationPlus(2*ϕ) + @test SingleExcitationPlus(ϕ) ^ -1 == inv(SingleExcitationPlus(ϕ)) + @test SingleExcitationPlus(ϕ) ^ - 3 == inv(SingleExcitationPlus(3*ϕ)) + end + + @testset "Single excitation minus" begin + ϕ = 3.56 + nq = 2 + instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(SingleExcitationMinus(ϕ), [0, 1])] + # instructions for the gate decomposition (from PennyLane) + de_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(X(), [0]), Instruction(X(), [1]), Instruction(CPhaseShift(-ϕ/2), [1, 0]), Instruction(X(), [0]), Instruction(X(), [1]), Instruction(CPhaseShift(-ϕ/2), [0, 1]), Instruction(CNot(), [0, 1]), Instruction(Ry(ϕ/2), [0]), Instruction(CNot(), [1, 0]), Instruction(Ry(-ϕ/2), [0]), Instruction(CNot(), [1, 0]), Instruction(CNot(), [0, 1])] + # instructions for the matrix representation + u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(SingleExcitationMinus(ϕ)))), [1, 0])] + # state vector for SingleExcitationMinus (from PennyLane) + state_vector = 0.5 * [exp(-im*ϕ/2), cos(ϕ/2) - sin(ϕ/2), cos(ϕ/2) + sin(ϕ/2), exp(-im*ϕ/2)] + probability_amplitudes = 0.25 * [1, (cos(ϕ/2) - sin(ϕ/2))^2, (cos(ϕ/2) + sin(ϕ/2))^2, 1] + @testset "Simulator $sim, instruction set $ix_label" for sim in (StateVectorSimulator, DensityMatrixSimulator), + (ix_label, ixs) in (("raw", instructions), ("decomp", de_instructions), ("unitary", u_instructions)) + simulation = sim(nq, 0) + simulation = evolve!(simulation, ixs) + if sim == StateVectorSimulator + @test state_vector ≈ collect(BraketSimulator.state_vector(simulation)) + end + @test probability_amplitudes ≈ + collect(BraketSimulator.probabilities(simulation)) + end + @test qubit_count(SingleExcitationMinus(ϕ)) == 2 + @test inv(SingleExcitationMinus(ϕ)) == SingleExcitationMinus(-ϕ) + @test SingleExcitationMinus(ϕ) ^ 0 == Braket.I() + @test SingleExcitationMinus(ϕ) ^ 2 == SingleExcitationMinus(2*ϕ) + @test SingleExcitationMinus(ϕ) ^ -1 == inv(SingleExcitationMinus(ϕ)) + @test SingleExcitationMinus(ϕ) ^ - 3 == inv(SingleExcitationMinus(3*ϕ)) + end + + @testset "Double excitation minus" begin + ϕ = 3.56 + nq = 4 + instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(DoubleExcitationMinus(ϕ), [0, 1, 2, 3])] + # instructions for the matrix representation + u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(DoubleExcitationMinus(ϕ)))), [0, 1, 2, 3])] + state_vector = 0.5 * [exp(-im*ϕ/2.0), 0, 0, -sin(ϕ/2), exp(-im*ϕ/2.0), 0, 0, 0, exp(-im*ϕ/2.0), 0, 0, 0, cos(ϕ/2), 0, 0, 0] + probability_amplitudes = 0.25 * [1, 0, 0, (-sin(ϕ/2))^2, 1, 0, 0, 0, 1, 0, 0, 0, (cos(ϕ/2))^2, 0, 0, 0] + @testset "Simulator $sim, instruction set $ix_label" for sim in (StateVectorSimulator, DensityMatrixSimulator), + (ix_label, ixs) in (("raw", instructions), ("unitary", u_instructions)) + simulation = sim(nq, 0) + simulation = evolve!(simulation, ixs) + if sim == StateVectorSimulator + @test state_vector ≈ collect(BraketSimulator.state_vector(simulation)) + end + @test probability_amplitudes ≈ + collect(BraketSimulator.probabilities(simulation)) + end + @test qubit_count(DoubleExcitationMinus(ϕ)) == 4 + @test inv(DoubleExcitationMinus(ϕ)) == DoubleExcitationMinus(-ϕ) + @test DoubleExcitationMinus(ϕ) ^ 0 == Braket.I() + @test DoubleExcitationMinus(ϕ) ^ 2 == DoubleExcitationMinus(2*ϕ) + @test DoubleExcitationMinus(ϕ) ^ -1 == inv(DoubleExcitationMinus(ϕ)) + @test DoubleExcitationMinus(ϕ) ^ - 3 == inv(DoubleExcitationMinus(3*ϕ)) + end + + @testset "Double excitation plus" begin + ϕ = 3.56 + nq = 4 + instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(DoubleExcitationPlus(ϕ), [0, 1, 2, 3])] + # instructions for the matrix representation + u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(DoubleExcitationPlus(ϕ)))), [0, 1, 2, 3])] + state_vector = 0.5 * [exp(im*ϕ/2.0), 0, 0, -sin(ϕ/2), exp(im*ϕ/2.0), 0, 0, 0, exp(im*ϕ/2.0), 0, 0, 0, cos(ϕ/2), 0, 0, 0] + probability_amplitudes = 0.25 * [1, 0, 0, (-sin(ϕ/2))^2, 1, 0, 0, 0, 1, 0, 0, 0, (cos(ϕ/2))^2, 0, 0, 0] + @testset "Simulator $sim, instruction set $ix_label" for sim in (StateVectorSimulator, DensityMatrixSimulator), + (ix_label, ixs) in (("raw", instructions), ("unitary", u_instructions)) + simulation = sim(nq, 0) + simulation = evolve!(simulation, ixs) + if sim == StateVectorSimulator + @test state_vector ≈ collect(BraketSimulator.state_vector(simulation)) + end + @test probability_amplitudes ≈ + collect(BraketSimulator.probabilities(simulation)) + end + @test qubit_count(DoubleExcitationPlus(ϕ)) == 4 + @test inv(DoubleExcitationPlus(ϕ)) == DoubleExcitationPlus(-ϕ) + @test DoubleExcitationPlus(ϕ) ^ 0 == Braket.I() + @test DoubleExcitationPlus(ϕ) ^ 2 == DoubleExcitationPlus(2*ϕ) + @test DoubleExcitationPlus(ϕ) ^ -1 == inv(DoubleExcitationPlus(ϕ)) + @test DoubleExcitationPlus(ϕ) ^ - 3 == inv(DoubleExcitationPlus(3*ϕ)) + end + + @testset "FermionicSWAP" begin + ϕ = 3.56 + nq = 2 + instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(FermionicSWAP(ϕ), [0, 1])] + # instructions for the matrix representation + u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(FermionicSWAP(ϕ)))), [1, 0])] + # state vector for FermionicSWAP (from PennyLane) + state_vector = 0.5 * [1, exp(im*ϕ/2.0)*cos(ϕ / 2.0) - im*exp(im*ϕ/2.0)*sin(ϕ/2.0), + - im*exp(im*ϕ/2.0)*sin(ϕ/2.0) + exp(im*ϕ/2.0)*cos(ϕ/2.0), exp(im * ϕ)] + probability_amplitudes = 0.25 * [1, (exp(im*ϕ/2.0)*cos(ϕ / 2.0) - im*exp(im*ϕ/2.0)*sin(ϕ/2.0))^2, + (-im*exp(im*ϕ/2.0)*sin(ϕ/2.0) + exp(im*ϕ/2.0)*cos(ϕ/2.0))^2, 1] + @testset "Simulator $sim, instruction set $ix_label" for sim in (StateVectorSimulator, DensityMatrixSimulator), + (ix_label, ixs) in (("raw", instructions), ("unitary", u_instructions)) + simulation = sim(nq, 0) + simulation = evolve!(simulation, ixs) + if sim == StateVectorSimulator + @test state_vector ≈ collect(BraketSimulator.state_vector(simulation)) + end + @test probability_amplitudes ≈ + collect(BraketSimulator.probabilities(simulation)) + end + @test qubit_count(FermionicSWAP(ϕ)) == 2 + @test inv(FermionicSWAP(ϕ)) == FermionicSWAP(-ϕ) + @test FermionicSWAP(ϕ) ^ 0 == Braket.I() + @test FermionicSWAP(ϕ) ^ 2 == FermionicSWAP(2*ϕ) + @test FermionicSWAP(ϕ) ^ -1 == inv(FermionicSWAP(ϕ)) + @test FermionicSWAP(ϕ) ^ - 3 == inv(FermionicSWAP(3*ϕ)) + end end From d9cdef208b2f3702cc28b2dd31bf592402667d28 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Wed, 9 Oct 2024 14:46:50 -0400 Subject: [PATCH 2/4] fix: Update to current gate API --- src/custom_gates.jl | 149 +++++++++++++++++--------------------- src/gate_kernels.jl | 4 +- test/test_custom_gates.jl | 74 +++++-------------- 3 files changed, 87 insertions(+), 140 deletions(-) diff --git a/src/custom_gates.jl b/src/custom_gates.jl index 87d84b6..d1123ba 100644 --- a/src/custom_gates.jl +++ b/src/custom_gates.jl @@ -83,31 +83,30 @@ julia> eq2 == [exp(im*ϕ/2), 0, 0, -sin(ϕ/2), 0, 0, 0, 0, 0, 0, 0, 0, cos(ϕ/2 """ struct DoubleExcitationPlus <: AngledGate{1} - angle::NTuple{1,Union{Float64,FreeParameter}} - DoubleExcitationPlus(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = - new(angle) + angle::NTuple{1,Union{Real,FreeParameter}} + pow_exponent::Float64 + DoubleExcitationPlus(angle::T, pow_exponent=1.0) where {T<:NTuple{1,Union{Real,FreeParameter}}} = + new(angle, Float64(pow_exponent)) end -Braket.chars(::Type{DoubleExcitationPlus}) = "G2+(ang)" -Braket.qubit_count(::Type{DoubleExcitationPlus}) = 4 -Base.inv(g::DoubleExcitationPlus) = DoubleExcitationPlus(-g.angle[1]) -Base.:^(g::DoubleExcitationPlus, power::Integer) = power == -1 ? inv(g) : (iszero(power) ? Braket.I() : (power < 0 ? inv(g^(-power)) : DoubleExcitationPlus((g.angle[1] * power,)))) -function matrix_rep(g::DoubleExcitationPlus) - cosϕ = cos(g.angle[1] / 2.0) - sinϕ = sin(g.angle[1] / 2.0) - eiϕ2 = exp(im * g.angle[1] / 2.0) - mat = diagm(eiϕ2 * ones(ComplexF64, 16)) - - mat[4, :] .= 0 - mat[:, 4] .= 0 - mat[13, :] .= 0 - mat[:, 13] .= 0 +qubit_count(::Type{DoubleExcitationPlus}) = 4 +function matrix_rep_raw(::DoubleExcitationPlus, ϕ) # nosemgrep + sϕ, cϕ = sincos(ϕ / 2.0) + eiϕ2 = exp(im * ϕ / 2.0) + mat = diagm(eiϕ2 * ones(ComplexF64, 16)) + @views begin + mat[4, :] .= 0 + mat[:, 4] .= 0 + mat[13, :] .= 0 + mat[:, 13] .= 0 + end # Apply phase-shift to states outside rotation subspace - mat[4, 4] = cosϕ - mat[13, 13] = cosϕ - mat[4, 13] = -sinϕ - mat[13, 4] = sinϕ + mat[4, 4] = cϕ + mat[13, 13] = cϕ + mat[4, 13] = -sϕ + mat[13, 4] = sϕ return SMatrix{16, 16, ComplexF64}(mat) end + """ DoubleExcitationMinus(ϕ) @@ -143,29 +142,27 @@ julia> eq2 == [exp(-im*ϕ/2), 0, 0, -sin(ϕ/2), 0, 0, 0, 0, 0, 0, 0, 0, cos(ϕ/ """ struct DoubleExcitationMinus <: AngledGate{1} - angle::NTuple{1,Union{Float64,FreeParameter}} - DoubleExcitationMinus(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = - new(angle) + angle::NTuple{1,Union{Real,FreeParameter}} + pow_exponent::Float64 + DoubleExcitationMinus(angle::T, pow_exponent=1.0) where {T<:NTuple{1,Union{Real,FreeParameter}}} = + new(angle, Float64(pow_exponent)) end -Braket.chars(::Type{DoubleExcitationMinus}) = "G2-(ang)" -Braket.qubit_count(::Type{DoubleExcitationMinus}) = 4 -Base.inv(g::DoubleExcitationMinus) = DoubleExcitationMinus(-g.angle[1]) -Base.:^(g::DoubleExcitationMinus, power::Integer) = power == -1 ? inv(g) : (iszero(power) ? Braket.I() : (power < 0 ? inv(g^(-power)) : DoubleExcitationMinus((g.angle[1] * power,)))) -function matrix_rep(g::DoubleExcitationMinus) - cosϕ = cos(g.angle[1] / 2.0) - sinϕ = sin(g.angle[1] / 2.0) - eiϕ2 = exp(-im * g.angle[1] / 2.0) - mat = diagm(eiϕ2 * ones(ComplexF64, 16)) - - mat[4, :] .= 0 - mat[:, 4] .= 0 - mat[13, :] .= 0 - mat[:, 13] .= 0 +qubit_count(::Type{DoubleExcitationMinus}) = 4 +function matrix_rep_raw(::DoubleExcitationMinus, ϕ) # nosemgrep + sϕ, cϕ = sincos(ϕ / 2.0) + eiϕ2 = exp(-im * ϕ / 2.0) + mat = diagm(eiϕ2 * ones(ComplexF64, 16)) + @views begin + mat[4, :] .= 0 + mat[:, 4] .= 0 + mat[13, :] .= 0 + mat[:, 13] .= 0 + end # Apply phase-shift to states outside rotation subspace - mat[4, 4] = cosϕ - mat[13, 13] = cosϕ - mat[4, 13] = -sinϕ - mat[13, 4] = sinϕ + mat[4, 4] = cϕ + mat[13, 13] = cϕ + mat[4, 13] = -sϕ + mat[13, 4] = sϕ return SMatrix{16, 16, ComplexF64}(mat) end @@ -234,19 +231,16 @@ julia> eq2 == [exp(im*ϕ/2), sin(ϕ/2), cos(ϕ/2), 0] == true; """ struct SingleExcitationPlus <: AngledGate{1} - angle::NTuple{1,Union{Float64,FreeParameter}} - SingleExcitationPlus(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = - new(angle) + angle::NTuple{1,Union{Real,FreeParameter}} + pow_exponent::Float64 + SingleExcitationPlus(angle::T, pow_exponent=1.0) where {T<:NTuple{1,Union{Real,FreeParameter}}} = + new(angle, Float64(pow_exponent)) end -Braket.chars(::Type{SingleExcitationPlus}) = "G+(ang)" -Braket.qubit_count(::Type{SingleExcitationPlus}) = 2 -Base.inv(g::SingleExcitationPlus) = SingleExcitationPlus(-g.angle[1]) -Base.:^(g::SingleExcitationPlus, power::Integer) = power == -1 ? inv(g) : (iszero(power) ? Braket.I() : (power < 0 ? inv(g^(-power)) : SingleExcitationPlus((g.angle[1] * power,)))) -function matrix_rep(g::SingleExcitationPlus) - cosϕ = cos(g.angle[1] / 2.0) - sinϕ = sin(g.angle[1] / 2.0) - eiϕ2 = exp(im * g.angle[1] / 2.0) - return SMatrix{4,4,ComplexF64}([eiϕ2 0 0 0; 0 cosϕ sinϕ 0; 0 -sinϕ cosϕ 0; 0 0 0 eiϕ2]) +qubit_count(::Type{SingleExcitationPlus}) = 2 +function matrix_rep_raw(::SingleExcitationPlus, ϕ) # nosemgrep + sϕ, cϕ = sincos(ϕ / 2.0) + eiϕ2 = exp(im * ϕ / 2.0) + return SMatrix{4,4,ComplexF64}(eiϕ2, 0, 0, 0, 0, cϕ, -sϕ, 0, 0, sϕ, cϕ, 0, 0, 0, 0, eiϕ2) end """ @@ -278,19 +272,16 @@ julia> eq2 == [exp(-im*ϕ/2), sin(ϕ/2), cos(ϕ/2), 0] == true; """ struct SingleExcitationMinus <: AngledGate{1} - angle::NTuple{1,Union{Float64,FreeParameter}} - SingleExcitationMinus(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = - new(angle) + angle::NTuple{1,Union{Real,FreeParameter}} + pow_exponent::Float64 + SingleExcitationMinus(angle::T, pow_exponent=1.0) where {T<:NTuple{1,Union{Real,FreeParameter}}} = + new(angle, Float64(pow_exponent)) end -Braket.chars(::Type{SingleExcitationMinus}) = "G-(ang)" -Braket.qubit_count(::Type{SingleExcitationMinus}) = 2 -Base.inv(g::SingleExcitationMinus) = SingleExcitationMinus(-g.angle[1]) -Base.:^(g::SingleExcitationMinus, power::Integer) = power == -1 ? inv(g) : (iszero(power) ? Braket.I() : (power < 0 ? inv(g^(-power)) : SingleExcitationMinus((g.angle[1] * power,)))) -function matrix_rep(g::SingleExcitationMinus) - cosϕ = cos(g.angle[1] / 2.0) - sinϕ = sin(g.angle[1] / 2.0) - eiϕ2 = exp(-im * g.angle[1] / 2.0) - return SMatrix{4,4,ComplexF64}([eiϕ2 0 0 0; 0 cosϕ sinϕ 0; 0 -sinϕ cosϕ 0; 0 0 0 eiϕ2]) +qubit_count(::Type{SingleExcitationMinus}) = 2 +function matrix_rep_raw(::SingleExcitationMinus, ϕ) + sϕ, cϕ = sincos(ϕ / 2.0) + eiϕ2 = exp(-im * ϕ / 2.0) + return SMatrix{4,4,ComplexF64}(eiϕ2, 0, 0, 0, 0, cϕ, -sϕ, 0, 0, sϕ, cϕ, 0, 0, 0, 0, eiϕ2) end """ @@ -337,22 +328,18 @@ julia> eq4 == [0, 0, 0, exp(im * ϕ)] == true; """ struct FermionicSWAP <: AngledGate{1} - angle::NTuple{1,Union{Float64,FreeParameter}} - FermionicSWAP(angle::T) where {T<:NTuple{1,Union{Float64,FreeParameter}}} = - new(angle) + angle::NTuple{1,Union{Real,FreeParameter}} + pow_exponent::Float64 + FermionicSWAP(angle::T, pow_exponent=1.0) where {T<:NTuple{1,Union{Real,FreeParameter}}} = + new(angle, Float64(pow_exponent)) end -Braket.chars(::Type{FermionicSWAP}) = "FSWAP(ang)" -Braket.qubit_count(::Type{FermionicSWAP}) = 2 -Base.inv(g::FermionicSWAP) = FermionicSWAP(-g.angle[1]) -Base.:^(g::FermionicSWAP, power::Integer) = power == -1 ? inv(g) : (power == 0 ? Braket.I() : (power < 0 ? inv(g^(-power)) : FermionicSWAP((g.angle[1] * power,)))) -function matrix_rep(g::FermionicSWAP) - cosϕ = cos(g.angle[1] / 2.0) - sinϕ = sin(g.angle[1] / 2.0) - eiϕ2 = exp(im * g.angle[1] / 2.0) - eiϕ = exp(im * g.angle[1]) - ieiϕ2 = im * eiϕ2 - - return SMatrix{4,4,ComplexF64}([1.0 0 0 0; 0 eiϕ2 * cosϕ -ieiϕ2 * sinϕ 0; 0 -ieiϕ2 * sinϕ eiϕ2 * cosϕ 0; 0 0 0 eiϕ]) +qubit_count(::Type{FermionicSWAP}) = 2 +function matrix_rep_raw(::FermionicSWAP, ϕ) # nosemgrep + sϕ, cϕ = sincos(ϕ / 2.0) + eiϕ2 = exp(im * ϕ / 2.0) + eiϕ = exp(im * ϕ) + ieiϕ2 = im * eiϕ2 + return SMatrix{4,4,ComplexF64}(1, 0, 0, 0, 0, eiϕ2 * cϕ, -ieiϕ2 * sϕ, 0, 0, -ieiϕ2*sϕ, eiϕ2*cϕ, 0, 0, 0, 0, eiϕ) end """ diff --git a/src/gate_kernels.jl b/src/gate_kernels.jl index 45c45bc..30039e8 100644 --- a/src/gate_kernels.jl +++ b/src/gate_kernels.jl @@ -116,7 +116,7 @@ for G in (:CPhaseShift, :CPhaseShift00, :CPhaseShift01, :CPhaseShift10, :ZZ) end end -for G in (:XX, :YY, :XY, :SingleExcitation) +for G in (:XX, :YY, :XY, :SingleExcitation, :SingleExcitationPlus, :SingleExcitationMinus, :FermionicSWAP) @eval function matrix_rep(g::$G) n = g.pow_exponent::Float64 θ = @inbounds g.angle[1] @@ -127,7 +127,7 @@ for G in (:XX, :YY, :XY, :SingleExcitation) end end -for G in (:DoubleExcitation, :MultiRZ) +for G in (:DoubleExcitation, :DoubleExcitationPlus, :DoubleExcitationMinus, :MultiRZ) @eval function matrix_rep(g::$G) n = g.pow_exponent::Float64 θ = @inbounds g.angle[1] diff --git a/test/test_custom_gates.jl b/test/test_custom_gates.jl index d62ac1b..d66d031 100644 --- a/test/test_custom_gates.jl +++ b/test/test_custom_gates.jl @@ -26,17 +26,9 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end - - @test qubit_count(DoubleExcitation(ϕ)) == 4 - @test inv(DoubleExcitation(ϕ)) == DoubleExcitation(-ϕ) - @test DoubleExcitation(ϕ) ^ 0 == Braket.I() - @test DoubleExcitation(ϕ) ^ 2 == DoubleExcitation(2*ϕ) - @test DoubleExcitation(ϕ) ^ -1 == inv(DoubleExcitation(ϕ)) - @test DoubleExcitation(ϕ) ^ - 3 == inv(DoubleExcitation(3*ϕ)) @test BraketSimulator.qubit_count(BraketSimulator.DoubleExcitation(ϕ)) == 4 @test BraketSimulator.qubit_count(BraketSimulator.DoubleExcitation) == 4 @test inv(BraketSimulator.DoubleExcitation(ϕ)) == BraketSimulator.DoubleExcitation(ϕ, -1.0) - end @testset "Single excitation" begin ϕ = 3.56 @@ -62,16 +54,9 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end - @test qubit_count(SingleExcitation(ϕ)) == 2 - @test inv(SingleExcitation(ϕ)) == SingleExcitation(-ϕ) - @test SingleExcitation(ϕ) ^ 0 == Braket.I() - @test SingleExcitation(ϕ) ^ 2 == SingleExcitation(2*ϕ) - @test SingleExcitation(ϕ) ^ -1 == inv(SingleExcitation(ϕ)) - @test SingleExcitation(ϕ) ^ - 3 == inv(SingleExcitation(3*ϕ)) @test BraketSimulator.qubit_count(BraketSimulator.SingleExcitation(ϕ)) == 2 @test BraketSimulator.qubit_count(BraketSimulator.SingleExcitation) == 2 @test inv(BraketSimulator.SingleExcitation(ϕ)) == BraketSimulator.SingleExcitation(ϕ, -1.0) - end @testset "3-angle U" begin θ = 1.34 @@ -195,11 +180,11 @@ using Test, Logging, BraketSimulator, DataStructures @testset "Single excitation plus" begin ϕ = 3.56 nq = 2 - instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(SingleExcitationPlus(ϕ), [0, 1])] + instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.SingleExcitationPlus(ϕ), [0, 1])] # instructions for the gate decomposition (from PennyLane) - de_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(X(), [0]), Instruction(X(), [1]), Instruction(CPhaseShift(ϕ/2), [1, 0]), Instruction(X(), [0]), Instruction(X(), [1]), Instruction(CPhaseShift(ϕ/2), [0, 1]), Instruction(CNot(), [0, 1]), Instruction(Ry(ϕ/2), [0]), Instruction(CNot(), [1, 0]), Instruction(Ry(-ϕ/2), [0]), Instruction(CNot(), [1, 0]), Instruction(CNot(), [0, 1])] + de_instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.X(), [0]), BraketSimulator.Instruction(BraketSimulator.X(), [1]), BraketSimulator.Instruction(BraketSimulator.CPhaseShift(ϕ/2), [1, 0]), BraketSimulator.Instruction(BraketSimulator.X(), [0]), BraketSimulator.Instruction(BraketSimulator.X(), [1]), BraketSimulator.Instruction(BraketSimulator.CPhaseShift(ϕ/2), [0, 1]), BraketSimulator.Instruction(BraketSimulator.CNot(), [0, 1]), BraketSimulator.Instruction(BraketSimulator.Ry(ϕ/2), [0]), BraketSimulator.Instruction(BraketSimulator.CNot(), [1, 0]), BraketSimulator.Instruction(BraketSimulator.Ry(-ϕ/2), [0]), BraketSimulator.Instruction(BraketSimulator.CNot(), [1, 0]), BraketSimulator.Instruction(BraketSimulator.CNot(), [0, 1])] # instructions for the matrix representation - u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(SingleExcitationPlus(ϕ)))), [1, 0])] + u_instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(BraketSimulator.matrix_rep(BraketSimulator.SingleExcitationPlus(ϕ)))), [0, 1])] # state vector for SingleExcitationPlus (from PennyLane) state_vector = 0.5 * [exp(im*ϕ/2), cos(ϕ/2) - sin(ϕ/2), cos(ϕ/2) + sin(ϕ/2), exp(im*ϕ/2)] probability_amplitudes = 0.25 * [1, (cos(ϕ/2) - sin(ϕ/2))^2, (cos(ϕ/2) + sin(ϕ/2))^2, 1] @@ -213,22 +198,17 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end - @test qubit_count(SingleExcitationPlus(ϕ)) == 2 - @test inv(SingleExcitationPlus(ϕ)) == SingleExcitationPlus(-ϕ) - @test SingleExcitationPlus(ϕ) ^ 0 == Braket.I() - @test SingleExcitationPlus(ϕ) ^ 2 == SingleExcitationPlus(2*ϕ) - @test SingleExcitationPlus(ϕ) ^ -1 == inv(SingleExcitationPlus(ϕ)) - @test SingleExcitationPlus(ϕ) ^ - 3 == inv(SingleExcitationPlus(3*ϕ)) + @test BraketSimulator.qubit_count(BraketSimulator.SingleExcitationPlus(ϕ)) == 2 end @testset "Single excitation minus" begin ϕ = 3.56 nq = 2 - instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(SingleExcitationMinus(ϕ), [0, 1])] + instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.SingleExcitationMinus(ϕ), [0, 1])] # instructions for the gate decomposition (from PennyLane) - de_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(X(), [0]), Instruction(X(), [1]), Instruction(CPhaseShift(-ϕ/2), [1, 0]), Instruction(X(), [0]), Instruction(X(), [1]), Instruction(CPhaseShift(-ϕ/2), [0, 1]), Instruction(CNot(), [0, 1]), Instruction(Ry(ϕ/2), [0]), Instruction(CNot(), [1, 0]), Instruction(Ry(-ϕ/2), [0]), Instruction(CNot(), [1, 0]), Instruction(CNot(), [0, 1])] + de_instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.X(), [0]), BraketSimulator.Instruction(BraketSimulator.X(), [1]), BraketSimulator.Instruction(BraketSimulator.CPhaseShift(-ϕ/2), [1, 0]), BraketSimulator.Instruction(BraketSimulator.X(), [0]), BraketSimulator.Instruction(BraketSimulator.X(), [1]), BraketSimulator.Instruction(BraketSimulator.CPhaseShift(-ϕ/2), [0, 1]), BraketSimulator.Instruction(BraketSimulator.CNot(), [0, 1]), BraketSimulator.Instruction(BraketSimulator.Ry(ϕ/2), [0]), BraketSimulator.Instruction(BraketSimulator.CNot(), [1, 0]), BraketSimulator.Instruction(BraketSimulator.Ry(-ϕ/2), [0]), BraketSimulator.Instruction(BraketSimulator.CNot(), [1, 0]), BraketSimulator.Instruction(BraketSimulator.CNot(), [0, 1])] # instructions for the matrix representation - u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(SingleExcitationMinus(ϕ)))), [1, 0])] + u_instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(BraketSimulator.matrix_rep(BraketSimulator.SingleExcitationMinus(ϕ)))), [0, 1])] # state vector for SingleExcitationMinus (from PennyLane) state_vector = 0.5 * [exp(-im*ϕ/2), cos(ϕ/2) - sin(ϕ/2), cos(ϕ/2) + sin(ϕ/2), exp(-im*ϕ/2)] probability_amplitudes = 0.25 * [1, (cos(ϕ/2) - sin(ϕ/2))^2, (cos(ϕ/2) + sin(ϕ/2))^2, 1] @@ -242,20 +222,15 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end - @test qubit_count(SingleExcitationMinus(ϕ)) == 2 - @test inv(SingleExcitationMinus(ϕ)) == SingleExcitationMinus(-ϕ) - @test SingleExcitationMinus(ϕ) ^ 0 == Braket.I() - @test SingleExcitationMinus(ϕ) ^ 2 == SingleExcitationMinus(2*ϕ) - @test SingleExcitationMinus(ϕ) ^ -1 == inv(SingleExcitationMinus(ϕ)) - @test SingleExcitationMinus(ϕ) ^ - 3 == inv(SingleExcitationMinus(3*ϕ)) + @test BraketSimulator.qubit_count(BraketSimulator.SingleExcitationMinus(ϕ)) == 2 end @testset "Double excitation minus" begin ϕ = 3.56 nq = 4 - instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(DoubleExcitationMinus(ϕ), [0, 1, 2, 3])] + instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.DoubleExcitationMinus(ϕ), [0, 1, 2, 3])] # instructions for the matrix representation - u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(DoubleExcitationMinus(ϕ)))), [0, 1, 2, 3])] + u_instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(BraketSimulator.matrix_rep(BraketSimulator.DoubleExcitationMinus(ϕ)))), [0, 1, 2, 3])] state_vector = 0.5 * [exp(-im*ϕ/2.0), 0, 0, -sin(ϕ/2), exp(-im*ϕ/2.0), 0, 0, 0, exp(-im*ϕ/2.0), 0, 0, 0, cos(ϕ/2), 0, 0, 0] probability_amplitudes = 0.25 * [1, 0, 0, (-sin(ϕ/2))^2, 1, 0, 0, 0, 1, 0, 0, 0, (cos(ϕ/2))^2, 0, 0, 0] @testset "Simulator $sim, instruction set $ix_label" for sim in (StateVectorSimulator, DensityMatrixSimulator), @@ -268,20 +243,15 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end - @test qubit_count(DoubleExcitationMinus(ϕ)) == 4 - @test inv(DoubleExcitationMinus(ϕ)) == DoubleExcitationMinus(-ϕ) - @test DoubleExcitationMinus(ϕ) ^ 0 == Braket.I() - @test DoubleExcitationMinus(ϕ) ^ 2 == DoubleExcitationMinus(2*ϕ) - @test DoubleExcitationMinus(ϕ) ^ -1 == inv(DoubleExcitationMinus(ϕ)) - @test DoubleExcitationMinus(ϕ) ^ - 3 == inv(DoubleExcitationMinus(3*ϕ)) + @test BraketSimulator.qubit_count(BraketSimulator.DoubleExcitationMinus(ϕ)) == 4 end @testset "Double excitation plus" begin ϕ = 3.56 nq = 4 - instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(DoubleExcitationPlus(ϕ), [0, 1, 2, 3])] + instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.DoubleExcitationPlus(ϕ), [0, 1, 2, 3])] # instructions for the matrix representation - u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(DoubleExcitationPlus(ϕ)))), [0, 1, 2, 3])] + u_instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(BraketSimulator.matrix_rep(BraketSimulator.DoubleExcitationPlus(ϕ)))), [0, 1, 2, 3])] state_vector = 0.5 * [exp(im*ϕ/2.0), 0, 0, -sin(ϕ/2), exp(im*ϕ/2.0), 0, 0, 0, exp(im*ϕ/2.0), 0, 0, 0, cos(ϕ/2), 0, 0, 0] probability_amplitudes = 0.25 * [1, 0, 0, (-sin(ϕ/2))^2, 1, 0, 0, 0, 1, 0, 0, 0, (cos(ϕ/2))^2, 0, 0, 0] @testset "Simulator $sim, instruction set $ix_label" for sim in (StateVectorSimulator, DensityMatrixSimulator), @@ -294,20 +264,15 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end - @test qubit_count(DoubleExcitationPlus(ϕ)) == 4 - @test inv(DoubleExcitationPlus(ϕ)) == DoubleExcitationPlus(-ϕ) - @test DoubleExcitationPlus(ϕ) ^ 0 == Braket.I() - @test DoubleExcitationPlus(ϕ) ^ 2 == DoubleExcitationPlus(2*ϕ) - @test DoubleExcitationPlus(ϕ) ^ -1 == inv(DoubleExcitationPlus(ϕ)) - @test DoubleExcitationPlus(ϕ) ^ - 3 == inv(DoubleExcitationPlus(3*ϕ)) + @test BraketSimulator.qubit_count(BraketSimulator.DoubleExcitationPlus(ϕ)) == 4 end @testset "FermionicSWAP" begin ϕ = 3.56 nq = 2 - instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(FermionicSWAP(ϕ), [0, 1])] + instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.FermionicSWAP(ϕ), [0, 1])] # instructions for the matrix representation - u_instructions = [Instruction(H(), [0]), Instruction(H(), [1]), Instruction(Unitary(Matrix(matrix_rep(FermionicSWAP(ϕ)))), [1, 0])] + u_instructions = [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.H(), [1]), BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(BraketSimulator.matrix_rep(BraketSimulator.FermionicSWAP(ϕ)))), [1, 0])] # state vector for FermionicSWAP (from PennyLane) state_vector = 0.5 * [1, exp(im*ϕ/2.0)*cos(ϕ / 2.0) - im*exp(im*ϕ/2.0)*sin(ϕ/2.0), - im*exp(im*ϕ/2.0)*sin(ϕ/2.0) + exp(im*ϕ/2.0)*cos(ϕ/2.0), exp(im * ϕ)] @@ -323,11 +288,6 @@ using Test, Logging, BraketSimulator, DataStructures @test probability_amplitudes ≈ collect(BraketSimulator.probabilities(simulation)) end - @test qubit_count(FermionicSWAP(ϕ)) == 2 - @test inv(FermionicSWAP(ϕ)) == FermionicSWAP(-ϕ) - @test FermionicSWAP(ϕ) ^ 0 == Braket.I() - @test FermionicSWAP(ϕ) ^ 2 == FermionicSWAP(2*ϕ) - @test FermionicSWAP(ϕ) ^ -1 == inv(FermionicSWAP(ϕ)) - @test FermionicSWAP(ϕ) ^ - 3 == inv(FermionicSWAP(3*ϕ)) + @test qubit_count(BraketSimulator.FermionicSWAP(ϕ)) == 2 end end From cd3c96501dcc5251dba46560a36803b17d7a7f96 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Wed, 9 Oct 2024 14:57:43 -0400 Subject: [PATCH 3/4] fix: semgrep --- src/custom_gates.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/custom_gates.jl b/src/custom_gates.jl index d1123ba..f2d023e 100644 --- a/src/custom_gates.jl +++ b/src/custom_gates.jl @@ -278,7 +278,7 @@ struct SingleExcitationMinus <: AngledGate{1} new(angle, Float64(pow_exponent)) end qubit_count(::Type{SingleExcitationMinus}) = 2 -function matrix_rep_raw(::SingleExcitationMinus, ϕ) +function matrix_rep_raw(::SingleExcitationMinus, ϕ) # nosemgrep sϕ, cϕ = sincos(ϕ / 2.0) eiϕ2 = exp(-im * ϕ / 2.0) return SMatrix{4,4,ComplexF64}(eiϕ2, 0, 0, 0, 0, cϕ, -sϕ, 0, 0, sϕ, cϕ, 0, 0, 0, 0, eiϕ2) From 06429b36453aa05df6ea4e644cc5edb17d76029e Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Wed, 9 Oct 2024 15:08:21 -0400 Subject: [PATCH 4/4] fix: docs and cleanup --- docs/src/custom_gates.md | 7 +++++++ src/custom_gates.jl | 12 ------------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/docs/src/custom_gates.md b/docs/src/custom_gates.md index 7cdfaf6..d37d19e 100644 --- a/docs/src/custom_gates.md +++ b/docs/src/custom_gates.md @@ -8,4 +8,11 @@ CurrentModule = BraketSimulator ```@docs BraketSimulator.MultiRZ +BraketSimulator.DoubleExcitation +BraketSimulator.DoubleExcitationMinus +BraketSimulator.DoubleExcitationPlus +BraketSimulator.SingleExcitation +BraketSimulator.SingleExcitationMinus +BraketSimulator.SingleExcitationPlus +BraketSimulator.FermionicSWAP ``` diff --git a/src/custom_gates.jl b/src/custom_gates.jl index f2d023e..3b70946 100644 --- a/src/custom_gates.jl +++ b/src/custom_gates.jl @@ -93,12 +93,6 @@ function matrix_rep_raw(::DoubleExcitationPlus, ϕ) # nosemgrep sϕ, cϕ = sincos(ϕ / 2.0) eiϕ2 = exp(im * ϕ / 2.0) mat = diagm(eiϕ2 * ones(ComplexF64, 16)) - @views begin - mat[4, :] .= 0 - mat[:, 4] .= 0 - mat[13, :] .= 0 - mat[:, 13] .= 0 - end # Apply phase-shift to states outside rotation subspace mat[4, 4] = cϕ mat[13, 13] = cϕ @@ -152,12 +146,6 @@ function matrix_rep_raw(::DoubleExcitationMinus, ϕ) # nosemgrep sϕ, cϕ = sincos(ϕ / 2.0) eiϕ2 = exp(-im * ϕ / 2.0) mat = diagm(eiϕ2 * ones(ComplexF64, 16)) - @views begin - mat[4, :] .= 0 - mat[:, 4] .= 0 - mat[13, :] .= 0 - mat[:, 13] .= 0 - end # Apply phase-shift to states outside rotation subspace mat[4, 4] = cϕ mat[13, 13] = cϕ