Skip to content

Commit

Permalink
Merge pull request #518 from QuantumBFS/jg/annotate-line
Browse files Browse the repository at this point in the history
Annotate a single line (for quantum error correction)
  • Loading branch information
GiggleLiu authored Jul 6, 2024
2 parents eabb0bf + 4665797 commit f652ff4
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 33 deletions.
2 changes: 1 addition & 1 deletion docs/src/man/registers.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ We also have some "cheating" version [`measure`](@ref) that does not collapse st
```@repl register
measure!(reg0, 1) # measure the qubit, the state collapses
measure!(reg0) # measure all qubits
measure(reg0, 3) # measure the qubit 3 times, the state does not collapse (hacky)
measure(reg0, 3) # measure the qubit at location 3, the state does not collapse (hacky)
reorder!(reg0, 7:-1:1) # reorder the qubits
measure!(reg0)
invorder!(reg0) # reverse the order of qubits
Expand Down
5 changes: 5 additions & 0 deletions lib/YaoBlocks/test/primitive/time_evolution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,17 @@ end
mpb = mat(pb)
allpass = true
for i=basis(pb), j=basis(pb)
allpass &= isapprox(pb[i, j], mpb[Int(i)+1, Int(j)+1]; atol=1e-6)
allpass &= isapprox(pb[i, j], mpb[Int(i)+1, Int(j)+1]; atol=1e-6, rtol=1e-6)
end
@test allpass

allpass = true
for j=basis(pb)
allpass &= isapprox(vec(pb[:, j]), mpb[:, Int(j)+1]; atol=1e-6)
allpass &= isapprox(vec(pb[j,:]), mpb[Int(j)+1,:]; atol=1e-6)
allpass &= isapprox(vec(pb[:, EntryTable([j], [1.0+0im])]), mpb[:, Int(j)+1]; atol=1e-6)
allpass &= isapprox(vec(pb[EntryTable([j], [1.0+0im]),:]), mpb[Int(j)+1,:]; atol=1e-6)
allpass &= isapprox(vec(pb[:, j]), mpb[:, Int(j)+1]; atol=1e-6, rtol=1e-6)
allpass &= isapprox(vec(pb[j,:]), mpb[Int(j)+1,:]; atol=1e-6, rtol=1e-6)
allpass &= isapprox(vec(pb[:, EntryTable([j], [1.0+0im])]), mpb[:, Int(j)+1]; atol=1e-6, rtol=1e-6)
Expand Down
4 changes: 1 addition & 3 deletions lib/YaoPlots/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ version = "0.9.2"
[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"
Thebes = "8b424ff8-82f5-59a4-86a6-de3761897198"
YaoArrayRegister = "e600142f-9330-5003-8abb-0ebd767abc51"
YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df"

[compat]
LinearAlgebra = "1"
Luxor = "3"
Thebes = "1"
Luxor = "4"
YaoArrayRegister = "0.9"
YaoBlocks = "0.13"
julia = "1"
Expand Down
2 changes: 1 addition & 1 deletion lib/YaoPlots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ using YaoPlots, Yao
reg = zero_state(1) |> Rx/8) |> Rx/8)
rho = density_matrix(ghz_state(2), 1)

bloch_sphere(""=>reg, "ρ"=>rho; show_projection_lines=true)
bloch_sphere(">"=>reg, "ρ"=>rho; show_projection_lines=true)
```

Similarly, you will see
Expand Down
100 changes: 100 additions & 0 deletions lib/YaoPlots/src/3d.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# The following code is copied from Thebes.jl, which is a package for 3D plotting in Julia.
# Since Thebes.jl is not updated for a while, we need to update the code to make it work with the latest version of Luxor.jl.

module Thebes
using Luxor
struct Point3D
x::Float64
y::Float64
z::Float64
end

mutable struct Projection
U::Point3D #
V::Point3D #
W::Point3D #
ue::Float64 #
ve::Float64 #
we::Float64 #
eyepoint::Point3D
centerpoint::Point3D
uppoint::Point3D
perspective::Float64 #
end

function newprojection(ipos::Point3D, center::Point3D, up::Point3D, perspective=0.0)
if iszero(ipos.x)
ipos = Point3D(10e-9, ipos.y, ipos.z)
end
if iszero(ipos.y)
ipos = Point3D(ipos.x, 10e-9, ipos.z)
end
if iszero(ipos.z)
ipos = Point3D(ipos.x, ipos.y, 10e-9)
end

# w is the line of sight
W = Point3D(center.x - ipos.x, center.y - ipos.y, center.z - ipos.z)
r = (W.x * W.x) + (W.y * W.y) + (W.z * W.z)
if r < eps()
@info("eye position and center are the same")

W = Point3D(0.0, 0.0, 0.0)
else
# distancealise w to unit length
rinv = 1 / sqrt(r)
W = Point3D(W.x * rinv, W.y * rinv, W.z * rinv)
end
we = W.x * ipos.x + W.y * ipos.y + W.z * ipos.z # project e on to w
U = Point3D(W.y * (up.z - ipos.z) - W.z * (up.y - ipos.y), # u is at right angles to t - e
W.z * (up.x - ipos.x) - W.x * (up.z - ipos.z), # and w ., its' the pictures x axis
W.x * (up.y - ipos.y) - W.y * (up.x - ipos.x))
r = (U.x * U.x) + (U.y * U.y) + (U.z * U.z)

if r < eps()
@info("struggling to make a valid projection with these parameters")
U = Point3D(0.0, 0.0, 0.0)
else
rinv = 1 / sqrt(r) # distancealise u
U = Point3D(U.x * rinv, U.y * rinv, U.z * rinv)
end

ue = U.x * ipos.x + U.y * ipos.y + U.z * ipos.z # project e onto u

V = Point3D(U.y * W.z - U.z * W.y, # v is at right angles to u and w
U.z * W.x - U.x * W.z, # it's the world's y axis
U.x * W.y - U.y * W.x)

ve = V.x * ipos.x + V.y * ipos.y + V.z * ipos.z # project e onto v

Projection(U, V, W, ue, ve, we, ipos, center, up, perspective)
end

function project(proj::Projection, P::Point3D)
# use default value for perspectiveness if not specified
r = proj.W.x * P.x + proj.W.y * P.y + proj.W.z * P.z - proj.we
if r < eps()
# "point $P is behind eye"
result = nothing
else
if proj.perspective == 0.0
depth = 1
else
depth = proj.perspective * (1 / r)
end
uq = depth * (proj.U.x * P.x + proj.U.y * P.y + proj.U.z * P.z - proj.ue)
vq = depth * (proj.V.x * P.x + proj.V.y * P.y + proj.V.z * P.z - proj.ve)
result = Point(uq, -vq) # because Y is down the page in Luxor (?!)
end
return result
end

function eyepoint(pt::Point3D)
return newprojection(pt, Point3D(0, 0, 0), Point3D(0, 0, 1))
end

function Luxor.distance(p1::Point3D, p2::Point3D)
sqrt((p2.x - p1.x)^2 + (p2.y - p1.y)^2 + (p2.z - p1.z)^2)
end

end
6 changes: 4 additions & 2 deletions lib/YaoPlots/src/YaoPlots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@ using YaoBlocks
using YaoBlocks.DocStringExtensions
using YaoArrayRegister
import Luxor
import Thebes
using Luxor: @layer, Point
using Thebes: Point3D, project
using LinearAlgebra: tr
using YaoBlocks

export CircuitStyles, vizcircuit, darktheme!, lighttheme!
export bloch_sphere, BlochStyles
export plot
export LabelBlock, addlabel, LineAnnotation, line_annotation

"""An alias of `vizcircuit`"""
plot(;kwargs...) = x->plot(x;kwargs...)
plot(blk::AbstractBlock; kwargs...) = vizcircuit(blk; kwargs...)

include("helperblock.jl")
include("vizcircuit.jl")
include("3d.jl")
using .Thebes: Point3D, project
include("bloch.jl")

end
35 changes: 18 additions & 17 deletions lib/YaoPlots/src/bloch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module BlochStyles
# generic config
const lw = Ref(1.0)
const textsize = Ref(16.0)
const fontfamily = Ref("monospace")
const fontfamily = Ref("JuliaMono")
const background_color = Ref("transparent")
const color = Ref("#000000")

Expand Down Expand Up @@ -132,8 +132,6 @@ function bloch_sphere(states...;
Luxor.fontsize(textsize)
fontfamily !== nothing && Luxor.fontface(fontfamily)
Luxor.setline(lw)
Thebes.eyepoint(eye_point...)

draw_bloch_sphere(states...; eye_point, extra_kwargs...)

# Save the drawing to a file
Expand All @@ -153,21 +151,23 @@ function draw_bloch_sphere(states::Pair{<:AbstractString}...;
colors = fill(BlochStyles.color[], length(states)),
extra_kwargs...
)
proj = Thebes.eyepoint(Point3D(eye_point...))

# get coordinate of a state
getcoo(x) = Point3D(ball_size .* state_to_cartesian(x))
getcoo(x) = Point3D((ball_size .* state_to_cartesian(x))...)

# ball
Luxor.circle(Point(0, 0), ball_size, :stroke)

# equator
nstep = 100
equator_points = map(LinRange(0, 2π*(1-1/nstep), nstep)) do ϕ
project(Point3D(ball_size .* polar_to_cartesian(1.0, π/2, ϕ)))
project(proj, Point3D((ball_size .* polar_to_cartesian(1.0, π/2, ϕ))...))
end
Luxor.line.(equator_points[1:2:end], equator_points[2:2:end], :stroke)

# show axes
axes3D(ball_size*3 ÷ 2; extra_kwargs...)
axes3D(ball_size*3 ÷ 2; proj, extra_kwargs...)

# show 01 states
if show01
Expand All @@ -178,7 +178,7 @@ function draw_bloch_sphere(states::Pair{<:AbstractString}...;
if Thebes.distance(Point3D(0, 0, 0), Point3D(eye_point...)) < Thebes.distance(p, Point3D(eye_point...))
Luxor.setopacity(0.3)
end
show_point(txt, project(p); dot_size, text_offset=Point(10, 0), show_line=false)
show_point(txt, project(proj, p); dot_size, text_offset=Point(10, 0), show_line=false)
end
end
end
Expand All @@ -191,32 +191,32 @@ function draw_bloch_sphere(states::Pair{<:AbstractString}...;
if Thebes.distance(Point3D(0, 0, 0), Point3D(eye_point...)) < Thebes.distance(p, Point3D(eye_point...))
Luxor.setopacity(0.3)
end
show_point(txt, project(p); dot_size, text_offset=Point(10, 0), show_line=show_line)
show_point(txt, project(proj, p); dot_size, text_offset=Point(10, 0), show_line=show_line)
end
if show_projection_lines
# show θ
ratio = 0.2
sz = project(Point3D(0, 0, ball_size*ratio))
sz = project(proj, Point3D(0, 0, ball_size*ratio))
if show_angle_texts
Luxor.move(sz)
Luxor.arc2r(Point(0, 0), sz, project(p) * ratio, :stroke)
Luxor.arc2r(Point(0, 0), sz, project(proj, p) * ratio, :stroke)
Luxor.text("θ", sz - Point(0, ball_size*0.07))
end
# show equator projection and ϕ
equatorp = Point3D(p[1], p[2], 0)
sx = project(Point3D(ball_size*ratio, 0, 0))
equatorp = Point3D(p.x, p.y, 0)
sx = project(proj, Point3D(ball_size*ratio, 0, 0))

if show_angle_texts
Luxor.move(sx)
Luxor.carc2r(Point(0, 0), sx, project(equatorp) * ratio, :stroke)
Luxor.carc2r(Point(0, 0), sx, project(proj, equatorp) * ratio, :stroke)
Luxor.text("ϕ", sx - Point(ball_size*0.12, 0))
end

@layer begin
Luxor.setdash("dot")
Luxor.setline(1)
Luxor.line(project(p), project(equatorp), :stroke)
Luxor.line(Point(0, 0), project(equatorp), :stroke)
Luxor.line(project(proj, p), project(proj, equatorp), :stroke)
Luxor.line(Point(0, 0), project(proj, equatorp), :stroke)
end
end
end
Expand Down Expand Up @@ -253,6 +253,7 @@ state_to_cartesian(state) = polar_to_cartesian(state_to_polar(state)...)

# Draw labelled 3D axes with length `n`.
function axes3D(n::Int;
proj,
axes_lw = BlochStyles.axes_lw[],
axes_textsize = BlochStyles.textsize[],
axes_colors = BlochStyles.axes_colors,
Expand All @@ -262,10 +263,10 @@ function axes3D(n::Int;
Luxor.fontsize(axes_textsize)
Luxor.setline(axes_lw)
for i = 1:3
axis1 = project(Point3D(0.1, 0.1, 0.1))
axis1 = project(proj, Point3D(0.1, 0.1, 0.1))
axis2 = [0.1, 0.1, 0.1]
axis2[i] = n
axis2 = project(Point3D(axis2...))
axis2 = project(proj, Point3D(axis2...))
Luxor.sethue(axes_colors[i])
if (axis1 !== nothing) && (axis2 !== nothing) && !isapprox(axis1, axis2)
Luxor.arrow(axis1, axis2)
Expand Down
18 changes: 14 additions & 4 deletions lib/YaoPlots/src/helperblock.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
using YaoBlocks
export LabelBlock

"""
LabelBlock{BT,D} <: TagBlock{BT,D}
Expand Down Expand Up @@ -31,7 +28,6 @@ Base.adjoint(x::LabelBlock) = LabelBlock(adjoint(content(x)), endswith(x.name, "
Base.copy(x::LabelBlock) = LabelBlock(copy(content(x)), x.name, x.color)
YaoBlocks.Optimise.to_basictypes(block::LabelBlock) = block

export addlabel
addlabel(b::AbstractBlock; name=string(b), color="transparent") = LabelBlock(b, name, color)

# to fix issue
Expand All @@ -49,3 +45,17 @@ function YaoBlocks.print_tree(
)
print(io, node.name)
end

"""
LineAnnotation{D} <: TrivialGate{D}
"""
struct LineAnnotation{D} <: TrivialGate{D}
name::String
color::String
end
line_annotation(name::String; color="black", nlevel=2) = LineAnnotation{nlevel}(name, color)

Base.copy(x::LineAnnotation) = LineAnnotation(x.name, x.color)
YaoBlocks.Optimise.to_basictypes(block::LineAnnotation) = block
YaoBlocks.nqudits(::LineAnnotation) = 1
YaoBlocks.print_block(io::IO, blk::LineAnnotation) = YaoBlocks.printstyled(io, blk.name; color=Symbol(blk.color))
21 changes: 17 additions & 4 deletions lib/YaoPlots/src/vizcircuit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module CircuitStyles
const lw = Ref(1.0)
const textsize = Ref(16.0)
const paramtextsize = Ref(10.0)
const fontfamily = Ref("monospace")
const fontfamily = Ref("JuliaMono")
#const fontfamily = Ref("Dejavu Sans")
const linecolor = Ref("#000000")
const gate_bgcolor = Ref("transparent")
Expand Down Expand Up @@ -313,10 +313,11 @@ end

# composite
function draw!(c::CircuitGrid, p::ChainBlock, address, controls)
CircuitStyles.barrier_for_chain[] && set_barrier!(c, Int[address..., controls...])
for block in subblocks(p)
draw!(c, block, address, controls)
CircuitStyles.barrier_for_chain[] && set_barrier!(c, Int[address..., controls...])
end
CircuitStyles.barrier_for_chain[] && set_barrier!(c, Int[address..., controls...])
end

function set_barrier!(c::CircuitGrid, locs::AbstractVector{Int})
Expand Down Expand Up @@ -374,6 +375,18 @@ function draw!(c::CircuitGrid, cb::LabelBlock, address, controls)
CircuitStyles.gate_bgcolor[] = temp
end

function draw!(c::CircuitGrid, cb::LineAnnotation, address, controls)
@assert length(address) == 1 && isempty(controls) "LineAnnotation should be a single line, without control."
CircuitStyles.textcolor[], temp = cb.color, CircuitStyles.textcolor[]
_annotate!(c, address[1], cb.name)
CircuitStyles.textcolor[] = temp
end
function _annotate!(c::CircuitGrid, loc::Integer, name::AbstractString)
wspace, fontsize = text_width_and_size(name)
i = frontier(c, loc) + 0.1
CircuitStyles.render(CircuitStyles.Text(fontsize), (c[i, loc-0.2], name, wspace, fontsize))
end

# [:KronBlock, :RepeatedBlock, :CachedBlock, :Subroutine, :(YaoBlocks.AD.NoParams)]
function draw!(c::CircuitGrid, p::CompositeBlock, address, controls)
barrier_style = CircuitStyles.barrier_for_chain[]
Expand Down Expand Up @@ -427,7 +440,7 @@ get_brush_texts(c, ::ConstGate.P0Gate) = (c.gatestyles.g, "P₀")
get_brush_texts(c, ::ConstGate.P1Gate) = (c.gatestyles.g, "P₁")
get_brush_texts(c, b::PrimitiveBlock) = (c.gatestyles.g, string(b))
get_brush_texts(c, b::TimeEvolution) = (c.gatestyles.g, string(b))
get_brush_texts(c, b::ShiftGate) = (c.gatestyles.g, "ϕ($(pretty_angle(b.theta)))")
get_brush_texts(c, b::ShiftGate) = (c.gatestyles.g, "φ($(pretty_angle(b.theta)))")
get_brush_texts(c, b::PhaseGate) = (CircuitStyles.Phase("$(pretty_angle(b.theta))"), "")
function get_brush_texts(c, b::T) where T<:ConstantGate
namestr = string(T.name.name)
Expand Down Expand Up @@ -468,7 +481,7 @@ They are defined as:
* CircuitStyles.lw = Ref(1.0) # line width
* CircuitStyles.textsize = Ref(16.0) # text size
* CircuitStyles.paramtextsize = Ref(10.0) # text size (longer texts)
* CircuitStyles.fontfamily = Ref("monospace") # font family
* CircuitStyles.fontfamily = Ref("JuliaMono") # font family
* CircuitStyles.linecolor = Ref("#000000") # line color
* CircuitStyles.gate_bgcolor = Ref("transparent") # gate background color
* CircuitStyles.textcolor = Ref("#000000") # text color
Expand Down
Loading

0 comments on commit f652ff4

Please sign in to comment.