diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d3e8d..2a11d9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## v0.4.12 - dev - Add Gabs extension for numerical translations of symbolic Gaussian states and operators. +- Add rotation gates `Rot[X,Y,Z]Gate`. ## v0.4.11 - 2025-06-22 diff --git a/ext/QuantumOpticsExt/QuantumOpticsExt.jl b/ext/QuantumOpticsExt/QuantumOpticsExt.jl index 0046ff6..c1a592e 100644 --- a/ext/QuantumOpticsExt/QuantumOpticsExt.jl +++ b/ext/QuantumOpticsExt/QuantumOpticsExt.jl @@ -4,7 +4,7 @@ using QuantumInterface, QuantumOpticsBase using QuantumInterface: samebases using QuantumSymbolics using QuantumSymbolics: - HGate, XGate, YGate, ZGate, CPHASEGate, CNOTGate, PauliP, PauliM, + HGate, XGate, YGate, ZGate, RotXGate, RotYGate, RotZGate, CPHASEGate, CNOTGate, PauliP, PauliM, XCXGate, XCYGate, XCZGate, YCXGate, YCYGate, YCZGate, ZCXGate, ZCYGate, ZCZGate, XBasisState, YBasisState, ZBasisState, NumberOp, CreateOp, DestroyOp, @@ -31,6 +31,9 @@ const _z = sigmaz(_b2) const _x = sigmax(_b2) const _y = sigmay(_b2) const _hadamard = (sigmaz(_b2)+sigmax(_b2))/√2 +_rotx(θ) = cos(θ/2)*_id - im*sin(θ/2)*_x +_roty(θ) = cos(θ/2)*_id - im*sin(θ/2)*_y +_rotz(θ) = cos(θ/2)*_id - im*sin(θ/2)*_z const _cnot = _l00⊗_id + _l11⊗_x const _cphase = _l00⊗_id + _l11⊗_z const _phase = _l00 + im*_l11 @@ -47,6 +50,9 @@ express_nolookup(::HGate, ::QuantumOpticsRepr) = _hadamard express_nolookup(::XGate, ::QuantumOpticsRepr) = _x express_nolookup(::YGate, ::QuantumOpticsRepr) = _y express_nolookup(::ZGate, ::QuantumOpticsRepr) = _z +express_nolookup(g::RotXGate, ::QuantumOpticsRepr) = _rotx(g.θ) +express_nolookup(g::RotYGate, ::QuantumOpticsRepr) = _roty(g.θ) +express_nolookup(g::RotZGate, ::QuantumOpticsRepr) = _rotz(g.θ) express_nolookup(::CPHASEGate, ::QuantumOpticsRepr) = _cphase express_nolookup(::CNOTGate, ::QuantumOpticsRepr) = _cnot diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 8b9da55..d56c086 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -42,12 +42,12 @@ export SymQObj,QObj, SConjugate,STranspose,SProjector,SDagger,SInvOperator,SExpOperator,SVec,STrace,SPartialTrace, MixedState,IdentityOp, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SBraKet,SOuterKetBra, - HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, + HGate,XGate,YGate,ZGate,RotXGate,RotYGate,RotZGate,CPHASEGate,CNOTGate, XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState,TwoSqueezedState,BosonicThermalState, NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,SqueezeOp, TwoSqueezeOp,BeamSplitterOp, XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate, - qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock, + qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock,qsimplify_rot, qexpand, isunitary, KrausRepr,kraus diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index 8b77b80..1250f1c 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -106,6 +106,40 @@ symbollabel(::HGate) = "H" ishermitian(::HGate) = true isunitary(::HGate) = true +abstract type AbstractRotGate <: AbstractSingleQubitGate end +ishermitian(::AbstractRotGate) = false +isunitary(::AbstractRotGate) = true +isexpr(::AbstractRotGate) = true +iscall(::AbstractRotGate) = true +arguments(x::AbstractRotGate) = [x.θ] + +"""Rotation around the X axis""" +@withmetadata struct RotXGate <: AbstractRotGate + θ +end +operation(::RotXGate) = RotXGate +head(::RotXGate) = :RotXGate +children(x::RotXGate) = [:RotXGate, x.θ] +symbollabel(x::RotXGate) = "Rx($(x.θ))" + +"""Rotation around the Y axis""" +@withmetadata struct RotYGate <: AbstractRotGate + θ +end +operation(::RotYGate) = RotYGate +head(::RotYGate) = :RotYGate +children(x::RotYGate) = [:RotYGate, x.θ] +symbollabel(x::RotYGate) = "Ry($(x.θ))" + +"""Rotation around the Z axis""" +@withmetadata struct RotZGate <: AbstractRotGate + θ +end +operation(::RotZGate) = RotZGate +head(::RotZGate) = :RotZGate +children(x::RotZGate) = [:RotZGate, x.θ] +symbollabel(x::RotZGate) = "Rz($(x.θ))" + @withmetadata struct CNOTGate <: AbstractTwoQubitGate end symbollabel(::CNOTGate) = "CNOT" ishermitian(::CNOTGate) = true diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 1664ffe..a928af9 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -109,7 +109,28 @@ RULES_FOCK = [ @rule(~o::_isa(TwoSqueezeOp) * ~k::isequal(vac ⊗ vac) => TwoSqueezedState((~o).z)) ] -RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] +RULES_ROT = [ + @rule(~r::_isa(RotXGate) => I where iszero((~r).θ)), + @rule(~r::_isa(RotYGate) => I where iszero((~r).θ)), + @rule(~r::_isa(RotZGate) => I where iszero((~r).θ)), + @rule(~r::_isa(RotXGate) => (-im*X) where (~r).θ in [π, 1π]), + @rule(~r::_isa(RotYGate) => (-im*Y) where (~r).θ in [π, 1π]), + @rule(~r::_isa(RotZGate) => (-im*Z) where (~r).θ in [π, 1π]), + @rule(~r::_isa(RotXGate) => try RotXGate(mod((~r).θ, 4π)) catch end), + @rule(~r::_isa(RotYGate) => try RotYGate(mod((~r).θ, 4π)) catch end), + @rule(~r::_isa(RotZGate) => try RotZGate(mod((~r).θ, 4π)) catch end), + @rule(~r::_isa(RotXGate) => try (~r).θ ≥ 2π ? -RotXGate((~r).θ - 2π) : nothing catch end), + @rule(~r::_isa(RotYGate) => try (~r).θ ≥ 2π ? -RotYGate((~r).θ - 2π) : nothing catch end), + @rule(~r::_isa(RotZGate) => try (~r).θ ≥ 2π ? -RotZGate((~r).θ - 2π) : nothing catch end), + @rule(~r1::_isa(RotXGate) * ~r2::_isa(RotXGate) => try RotXGate((~r1).θ + (~r2).θ) catch end), + @rule(~r1::_isa(RotYGate) * ~r2::_isa(RotYGate) => try RotYGate((~r1).θ + (~r2).θ) catch end), + @rule(~r1::_isa(RotZGate) * ~r2::_isa(RotZGate) => try RotZGate((~r1).θ + (~r2).θ) catch end), + @rule(exp(~α * ~x::_isa(XGate)) => try if real(~α) == 0 RotXGate(-2imag(~α)) end catch end), + @rule(exp(~α * ~x::_isa(YGate)) => try if real(~α) == 0 RotYGate(-2imag(~α)) end catch end), + @rule(exp(~α * ~x::_isa(ZGate)) => try if real(~α) == 0 RotZGate(-2imag(~α)) end catch end) +] + +RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK; RULES_ROT] ## # Simplification rewriters @@ -119,6 +140,7 @@ qsimplify_pauli = Chain(RULES_PAULI) qsimplify_commutator = Chain(RULES_COMMUTATOR) qsimplify_anticommutator = Chain(RULES_ANTICOMMUTATOR) qsimplify_fock = Chain(RULES_FOCK) +qsimplify_rot = Chain(RULES_ROT) """ qsimplify(s; rewriter=nothing) diff --git a/test/test_rotation.jl b/test/test_rotation.jl new file mode 100644 index 0000000..25fd210 --- /dev/null +++ b/test/test_rotation.jl @@ -0,0 +1,76 @@ +@testitem "Test Rotation" begin + @testset "express tests" begin + using QuantumOptics + b = SpinBasis(1//2) + + @test isapprox(express(RotXGate(π/2)), Operator(b, [1 -im; -im 1]/√2)) + @test isapprox(express(RotYGate(π/2)), Operator(b, [1 -1; 1 1]/√2)) + @test isapprox(express(RotZGate(π/2)), Operator(b, [1-im 0; 0 1+im]/√2)) + end + + @testset "Identity tests" begin + @test isequal(qsimplify(RotXGate(0), rewriter=qsimplify_rot), I) + @test isequal(qsimplify(RotYGate(0), rewriter=qsimplify_rot), I) + @test isequal(qsimplify(RotZGate(0), rewriter=qsimplify_rot), I) + end + + @testset "Pauli tests" begin + @test isequal(qsimplify(RotXGate(π), rewriter=qsimplify_rot), -im*X) + @test isequal(qsimplify(RotYGate(π), rewriter=qsimplify_rot), -im*Y) + @test isequal(qsimplify(RotZGate(π), rewriter=qsimplify_rot), -im*Z) + + @test isequal(qsimplify(RotXGate(1π), rewriter=qsimplify_rot), -im*X) + @test isequal(qsimplify(RotYGate(1π), rewriter=qsimplify_rot), -im*Y) + @test isequal(qsimplify(RotZGate(1π), rewriter=qsimplify_rot), -im*Z) + end + + @testset "Fusion tests" begin + @test isequal(qsimplify(RotXGate(π/3) * RotXGate(π/3), rewriter=qsimplify_rot), RotXGate(2π/3)) + @test isequal(qsimplify(RotYGate(π/3) * RotYGate(π/3), rewriter=qsimplify_rot), RotYGate(2π/3)) + @test isequal(qsimplify(RotZGate(π/3) * RotZGate(π/3), rewriter=qsimplify_rot), RotZGate(2π/3)) + + @test isequal(qsimplify(RotXGate(π/2) * RotXGate(-π/2), rewriter=qsimplify_rot), I) + @test isequal(qsimplify(RotYGate(π/2) * RotYGate(-π/2), rewriter=qsimplify_rot), I) + @test isequal(qsimplify(RotZGate(π/2) * RotZGate(-π/2), rewriter=qsimplify_rot), I) + + @test isequal(qsimplify(2 * RotXGate(π/2) * RotXGate(π/2), rewriter=qsimplify_rot), -2im*X) + @test isequal(qsimplify(2 * RotYGate(π/2) * RotYGate(π/2), rewriter=qsimplify_rot), -2im*Y) + @test isequal(qsimplify(2 * RotZGate(π/2) * RotZGate(π/2), rewriter=qsimplify_rot), -2im*Z) + end + + @testset "Modulo tests" begin + @test isequal(qsimplify(RotXGate(2π), rewriter=qsimplify_rot), -I) + @test isequal(qsimplify(RotYGate(2π), rewriter=qsimplify_rot), -I) + @test isequal(qsimplify(RotZGate(2π), rewriter=qsimplify_rot), -I) + + @test isequal(qsimplify(RotXGate(3π), rewriter=qsimplify_rot), im*X) + @test isequal(qsimplify(RotYGate(3π), rewriter=qsimplify_rot), im*Y) + @test isequal(qsimplify(RotZGate(3π), rewriter=qsimplify_rot), im*Z) + + @test isequal(qsimplify(RotXGate(4π), rewriter=qsimplify_rot), I) + @test isequal(qsimplify(RotYGate(4π), rewriter=qsimplify_rot), I) + @test isequal(qsimplify(RotZGate(4π), rewriter=qsimplify_rot), I) + + @test isequal(qsimplify(RotXGate(5π), rewriter=qsimplify_rot), -im*X) + @test isequal(qsimplify(RotYGate(5π), rewriter=qsimplify_rot), -im*Y) + @test isequal(qsimplify(RotZGate(5π), rewriter=qsimplify_rot), -im*Z) + + @test isequal(qsimplify(RotXGate(π) * RotXGate(π), rewriter=qsimplify_rot), -I) + @test isequal(qsimplify(RotYGate(π) * RotYGate(π), rewriter=qsimplify_rot), -I) + @test isequal(qsimplify(RotZGate(π) * RotZGate(π), rewriter=qsimplify_rot), -I) + + @test isequal(qsimplify(2 * RotXGate(3π/2) * RotXGate(2π/2), rewriter=qsimplify_rot), -2RotXGate(π/2)) + @test isequal(qsimplify(2 * RotYGate(3π/2) * RotYGate(2π/2), rewriter=qsimplify_rot), -2RotYGate(π/2)) + @test isequal(qsimplify(2 * RotZGate(3π/2) * RotZGate(2π/2), rewriter=qsimplify_rot), -2RotZGate(π/2)) + end + + @testset "Exponential tests" begin + @test isequal(qsimplify(exp(-im * π/2 * X), rewriter=qsimplify_rot), -im*X) + @test isequal(qsimplify(exp(-im * π/2 * Y), rewriter=qsimplify_rot), -im*Y) + @test isequal(qsimplify(exp(-im * π/2 * Z), rewriter=qsimplify_rot), -im*Z) + + @test isequal(qsimplify(2 * exp(-im * 2π/2 * X) * exp(-im * 5π/2 * X), rewriter=qsimplify_rot), 2im*X) + @test isequal(qsimplify(2 * exp(-im * 2π/2 * Y) * exp(-im * 5π/2 * Y), rewriter=qsimplify_rot), 2im*Y) + @test isequal(qsimplify(2 * exp(-im * 2π/2 * Z) * exp(-im * 5π/2 * Z), rewriter=qsimplify_rot), 2im*Z) + end +end \ No newline at end of file