-
Notifications
You must be signed in to change notification settings - Fork 123
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
Register: Refactor #17
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c9bd08c
Register: delete view
Roger-luo c8846d1
Register: refactor
Roger-luo 44768a7
change test
Roger-luo e48d11a
Utils: improve performance
Roger-luo 9d1cbed
Test: rename pack
Roger-luo b398a76
Register: export new API nactive
Roger-luo 71f872a
Test: improve test
Roger-luo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,206 +1,148 @@ | ||
""" | ||
AbstractRegister{M, B, T, N} <: AbstractArray{T, N} | ||
AbstractRegister{M, B, T} | ||
|
||
Abstract type for quantum registers, all quantum registers supports | ||
the interface of julia arrays. | ||
Abstract type for quantum registers, all quantum registers contains a | ||
subtype of `AbstractArray` as member `state`. | ||
|
||
## Parameters | ||
- `M` is the number of qubits | ||
- `N` is the number of qubits | ||
- `B` is the batch size | ||
- `N` is the actual dimension (number of packed legs, batch dimension is the last dimension if batch size is not 0). | ||
- `T` eltype | ||
""" | ||
abstract type AbstractRegister{M, B, T, N} <: AbstractArray{T, N} end | ||
abstract type AbstractRegister{N, B, T} end | ||
|
||
export nqubit, nbatch, line_orders, state, nactive | ||
# register properties | ||
nqubit(reg::AbstractRegister{M}) where M = M | ||
nbatch(reg::AbstractRegister{M, B}) where {M, B} = B | ||
# We assume each register has a member named data and ids | ||
nqubit(reg::AbstractRegister{N}) where N = N | ||
nbatch(reg::AbstractRegister{N, B}) where {N, B} = B | ||
nactive(reg::AbstractRegister) = log2i(size(state(reg), 1)) | ||
# We assume each register has a member named state and orders | ||
# overload this if there is not | ||
qubits(reg::AbstractRegister{M}) where M = reg.ids | ||
data(reg::AbstractRegister) = reg.data | ||
|
||
# provide view method if data type supports | ||
export view_batch | ||
import Base: view | ||
view_batch(reg::AbstractRegister{M, B, T, N}, ibatch::Int) where {M, B, T, N} = | ||
view(data(reg), ntuple(x->:, Val{N-1})..., ibatch) | ||
view_batch(reg::AbstractRegister{M, 1, T, N}, ibatch::Int) where {M, T, N} = | ||
view(data(reg), ntuple(x->:, Val{N})...) | ||
view(reg::AbstractRegister, dims...) = view(data(reg), dims...) | ||
|
||
|
||
# use array interface | ||
# TODO: use @forward instead | ||
import Base: eltype, length, ndims, size, eachindex, | ||
getindex, setindex!, stride, strides, copy | ||
import Compat: axes | ||
|
||
eltype(x::AbstractRegister{M, B, T, N}) where {M, B, T, N} = T | ||
length(x::AbstractRegister) = length(data(x)) | ||
ndims(x::AbstractRegister) = ndims(data(x)) | ||
size(x::AbstractRegister) = size(data(x)) | ||
size(x::AbstractRegister, n::Integer) = size(data(x), n) | ||
axes(x::AbstractRegister) = axes(data(x)) | ||
axes(x::AbstractRegister, d::Integer) = axes(data(x), d) | ||
eachindex(x::AbstractRegister) = eachindex(data(x)) | ||
stride(x::AbstractRegister, k::Integer) = stride(data(x), k) | ||
strides(x::AbstractRegister) = strides(data(x)) | ||
getindex(x::AbstractRegister, index::Integer...) = getindex(data(x), index...) | ||
getindex(x::AbstractRegister, index::NTuple{N, T}) where {N, T <: Integer} = getindex(data(x), index...) | ||
setindex!(x::AbstractRegister, val, index::Integer...) = (setindex!(data(x), val, index...); x) | ||
setindex!(x::AbstractRegister, val, index::NTuple{N, T}) where {N, T <: Integer} = (setindex!(data(x), val, index...); x) | ||
copy(x::AbstractRegister{M, B, T, N}) where {M, B, T, N} = Register{M, B, T, N}(copy(data(x)), copy(qubits(x))) | ||
|
||
################# | ||
# Batch Iterator | ||
################# | ||
|
||
export batch | ||
|
||
# batch iterator | ||
struct BatchIter{R <: AbstractRegister} | ||
reg::R | ||
end | ||
|
||
batch(x::AbstractRegister) = BatchIter(x) | ||
|
||
import Base: start, next, done, length, eltype | ||
line_orders(reg::AbstractRegister{N}) where N = reg.line_orders | ||
state(reg::AbstractRegister) = reg.state | ||
|
||
start(x::BatchIter) = 1 | ||
next(x::BatchIter, state) = | ||
view_batch(x.reg, state), state+1 | ||
done(x::BatchIter, state) = state > length(x) | ||
length(x::BatchIter{R}) where R = nbatch(x.reg) | ||
import Base: eltype, copy | ||
eltype(::AbstractRegister{N, B, T}) where {N, B, T} = T | ||
function copy(reg::AbstractRegister) end | ||
|
||
export pack! | ||
# permutation and concentration of qubits | ||
|
||
""" | ||
pack!(dst, src, ids) | ||
pack_orders!(reg, orders) | ||
|
||
pack `ids` together to the first k-dimensions. | ||
pack `orders` together to the first k-dimensions. | ||
""" | ||
function pack!(dst, src, ids) end | ||
pack!(reg::AbstractRegister, ids::NTuple) = pack!(reg, reg, ids) | ||
pack!(reg::AbstractRegister, ids::Integer...) = pack!(reg, ids) | ||
function pack_orders! end | ||
|
||
|
||
export focus | ||
export focus! | ||
|
||
""" | ||
focus(register, ids...) | ||
focus!(register, orders) | ||
|
||
pack tensor legs with ids together and reshape the register to | ||
(exposed, remain, batch) or (exposed, remain) depending on register | ||
type (with or without batch). | ||
pack tensor legs with given orders and reshape the register state to | ||
(exposed, remain * batch). `orders` can be a `Vector` or a `Tuple`. It | ||
should contain either a `UnitRange` or `Int`. `UnitRange` will be | ||
considered as a contiguous quantum memory and `Int` will be considered | ||
as an in-contiguous order. | ||
""" | ||
focus(reg::AbstractRegister, ids...) = focus(reg, ids) | ||
function focus! end | ||
|
||
export Register | ||
""" | ||
Register{M, B, T, N} <: AbstractRegister{M, B, T, N} | ||
Register{N, B, T} <: AbstractRegister{N, B, T} | ||
|
||
default register type. This register use a builtin array | ||
to store the quantum state. | ||
to store the quantum state. The elements inside an instance | ||
of `Register` will be related to a certain memory address, | ||
but since it is not immutable (we need to change its shape), | ||
be careful not to change its state, though the behaviour is | ||
the same, but allocation should be avoided. Therefore, no | ||
shallow copy method is provided. | ||
""" | ||
struct Register{M, B, T, N} <: AbstractRegister{M, B, T, N} | ||
data::Array{T, N} | ||
ids::Vector{Int} | ||
mutable struct Register{N, B, T} <: AbstractRegister{N, B, T} | ||
state::Array{T, 2} | ||
line_orders::Vector{Int} | ||
end | ||
|
||
# We store the state with a M-dimentional tensor by default | ||
# This will reduce memory allocation by allowing us use | ||
# permutedims! rather than permutedims. | ||
|
||
# Type Inference | ||
Register(nqubit::Int, nbatch::Int, data::Array{T, N}, ids::Vector{Int}) where {T, N} = | ||
Register{nqubit, nbatch, T, N}(data, ids) | ||
# type conversion | ||
Register(nqubit::Integer, nbatch::Integer, data::Array, ids) = | ||
Register(Int(nqubit), Int(nbatch), data, Int[ids...]) | ||
|
||
Register(nqubit::Integer, nbatch::Integer, data::Array) = | ||
Register(nqubit, nbatch, data, 1:nqubit) | ||
|
||
# calculate number of qubits from data array | ||
function Register(nbatch::Integer, data::Array) | ||
len = length(data) ÷ nbatch | ||
ispow2(len) || throw(Compat.InexactError(:Register, Register, data)) | ||
Register(log2i(len), nbatch, data) | ||
function Register(state::Array{T, 2}) where T | ||
len, nbatch = size(state) | ||
ispow2(len) || throw(Compat.InexactError(:Register, Register, state)) | ||
N = log2i(len) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this realization of log2i is much better: https://groups.google.com/forum/#!topic/julia-users/YaACmwePGxM |
||
Register{N, nbatch, T}(state, collect(1:N)) | ||
end | ||
|
||
# no batch | ||
Register(data::Array) = Register(1, data) | ||
|
||
######################### | ||
# Default Initialization | ||
######################### | ||
|
||
# NOTE: we store a rank-n tensor for n qubits by default, | ||
# we can always optimize this by add other kind of | ||
# register for specific tasks | ||
Register(::Type{T}, nqubit::Integer, nbatch::Integer) where T = | ||
Register(nqubit, nbatch, zeros(T, ntuple(x->2, Val{nqubit})..., nbatch), 1:nqubit) | ||
Register(::Type{T}, nqubit::Integer) where T = | ||
Register(nqubit, 1, zeros(T, ntuple(x->2, Val{nqubit})...), 1:nqubit) | ||
Register(state::Array{T, 1}) where T = Register(reshape(state, length(state), 1)) | ||
|
||
# We use Compelx128 by default | ||
Register(nqubit::Integer, nbatch::Integer) = | ||
Register(Complex128, nqubit, nbatch) | ||
Register(nqubit::Integer) = | ||
Register(Complex128, nqubit) | ||
export zero_state, rand_state | ||
zero_state(::Type{T}, nqubit::Int, nbatch::Int=1) where T = | ||
(state = zeros(T, 1<<nqubit, nbatch); state[1, :] = 1; Register(state)) | ||
# TODO: support different RNG?, default is a MT19937 | ||
rand_state(::Type{T}, nqubit::Int, nbatch::Int=1) where T = Register(rand(T, 1<<nqubit, nbatch)) | ||
|
||
# set default type | ||
zero_state(nqubit::Int, nbatch::Int=1) = zero_state(Complex128, nqubit, nbatch) | ||
rand_state(nqubit::Int, nbatch::Int=1) = rand_state(Complex128, nqubit, nbatch) | ||
|
||
## Dimension Permutation & Reshape | ||
import Base: reshape | ||
copy(reg::Register{N, B, T}) where {N, B, T} = Register{N, B, T}(copy(reg.state), copy(reg.line_orders)) | ||
|
||
reshape(reg::Register{M, B, T, N}, dims::Dims) where {M, B, T, N} = | ||
Register(M, B, reshape(reg.data, dims), reg.ids) | ||
|
||
function pack!(dst::Register{M, B, T, N}, src::Register{M, B, T, N}, ids::NTuple{K, Int}) where {M, B, T, N, K} | ||
@assert N == M+1 "register shape mismatch" | ||
|
||
ids = sort!([ids...]) | ||
inds = findin(src.ids, ids) | ||
perm = copy(src.ids) | ||
## Dimension Permutation | ||
# in-contiguous orders | ||
function pack_orders!(reg::Register{N, B}, orders::T) where {N, B, T <: Union{Int, Vector{Int}}} | ||
tensor = reshape(state(reg), ntuple(x->2, Val{N})..., B) | ||
inds = findin(reg.line_orders, orders) | ||
perm = reg.line_orders | ||
deleteat!(perm, inds) | ||
prepend!(perm, ids) | ||
dst.ids .= perm | ||
append!(perm, M+1) | ||
permutedims!(dst.data, src.data, perm) | ||
dst | ||
prepend!(perm, orders) | ||
permutedims!(tensor, tensor, (perm..., N+1)) | ||
reg | ||
end | ||
|
||
function pack!(dst::Register{M, 1, T, M}, src::Register{M, 1, T, M}, ids::NTuple{K, Int}) where {M, T, K} | ||
ids = sort!([ids...]) | ||
inds = findin(src.ids, ids) | ||
perm = copy(src.ids) | ||
deleteat!(perm, inds) | ||
prepend!(perm, ids) | ||
dst.ids .= perm | ||
permutedims!(dst.data, src.data, perm) | ||
dst | ||
# contiguous orders | ||
function pack_orders!(reg::Register{N, B}, orders::UnitRange{Int}) where {N, B} | ||
start_order = first(orders) | ||
# return ASAP when target order at the beginning | ||
start_order == first(line_orders(reg)) && return reg | ||
|
||
pack_size = 1<<length(orders) | ||
start_ind, = findin(line_orders(reg), start_order) | ||
|
||
if start_ind+length(orders)-1 == N | ||
src = reshape(state(reg), :, pack_size) | ||
dst = reshape(state(reg), pack_size, :) | ||
transpose!(dst, src) | ||
else | ||
src = reshape(state(reg), 1<<start_ind, pack_size, :) | ||
dst = reshape(state(reg), pack_size, 1<<start_ind, :) | ||
permutedims!(dst, src, [2, 1, 3]) | ||
end | ||
deleteat!(reg.line_orders, start_ind:(start_ind + length(orders) - 1)) | ||
prepend!(reg.line_orders, collect(orders)) | ||
reg | ||
end | ||
|
||
function focus(src::Register{M, B, T, N}, ids::NTuple{K, Int}) where {M, N, B, T, K} | ||
N == M+1 || (src = reshape(src, ntuple(x->2, Val{M})..., B)) | ||
|
||
pack!(src, ids) | ||
exposed_size = 2^K | ||
remained_size = 2^(M-K) | ||
data = reshape(src.data, exposed_size, remained_size, B) | ||
Register(M, B, data, src.ids) | ||
# mixed | ||
function pack_orders!(reg::Register, orders) | ||
for each in reverse(orders) | ||
pack_orders!(reg, each) | ||
end | ||
reg | ||
end | ||
|
||
function focus(src::Register{M, 1, T, M}, ids::NTuple{K, Int}) where {M, T, K} | ||
pack!(src, ids) | ||
exposed_size = 2^K | ||
remained_size = 2^(M-K) | ||
data = reshape(src.data, exposed_size, remained_size) | ||
Register(M, 1, data, src.ids) | ||
nexposed(orders::NTuple{K, Int}) where K = 1<<K | ||
nexposed(orders::Vector{Int}) = 1<<length(orders) | ||
nexposed(orders::UnitRange{Int}) = 1<<length(orders) | ||
# mixed | ||
function nexposed(orders) | ||
total = 0 | ||
for each in orders | ||
total += length(each) | ||
end | ||
1<<total | ||
end | ||
|
||
# focus(src::Register{M, B}, ids::NTuple) where {M, B} = | ||
# focus(reshape(src, ntuple(x->2, Val{M})..., B), ids) | ||
focus(src::Register{M, 1}, ids::NTuple) where M = | ||
focus(reshape(src, ntuple(x->2, Val{M})), ids) | ||
function focus!(reg::Register{N, B}, orders) where {N, B} | ||
pack_orders!(reg, orders) | ||
reg.state = reshape(state(reg), (nexposed(orders), :)) | ||
reg | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made a mistake, now I think rank-3 tensor makes more sense, because in this way, the array shape contains all informations about qubit and batch. otherwise, we can not distinguish remaining dimension and batch dimension without query the register.