Skip to content

Commit 2b2e37a

Browse files
author
alexander papageorge
committed
functional embedding with new syntax
1 parent 2a0e0b3 commit 2b2e37a

File tree

2 files changed

+60
-44
lines changed

2 files changed

+60
-44
lines changed

src/operators.jl

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -90,48 +90,58 @@ tensor(op::AbstractOperator) = op
9090
tensor(operators::AbstractOperator...) = reduce(tensor, operators)
9191

9292

93+
94+
"""
95+
docstring
96+
"""
97+
function check_indices(indices::Array)
98+
status = true
99+
# Check that no sub-list contains duplicates.
100+
for i in filter(x -> typeof(x) <: Array, indices)
101+
status &= (length(Set(i)) == length(i))
102+
end
103+
# Check that there are no duplicates across `indices`
104+
for (idx, i) in enumerate(indices[1:end-1])
105+
for j in indices[idx+1:end]
106+
status &= (length(intersect(i, j)) == 0)
107+
end
108+
end
109+
return status
110+
end
111+
112+
93113
"""
94114
embed(basis1[, basis2], indices::Vector, operators::Vector)
95115
96116
Tensor product of operators where missing indices are filled up with identity operators.
97117
"""
98118
function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
99-
indices::Vector{Int}, operators::Vector{T}) where T<:AbstractOperator
119+
indices::Vector, operators::Vector{T}) where T<:AbstractOperator
120+
121+
@assert check_indices(indices)
122+
100123
N = length(basis_l.bases)
101124
@assert length(basis_r.bases) == N
102-
@assert length(indices) >= length(operators)
103-
op_b_len = Int[isa(op.basis_l, CompositeBasis) ? length(op.basis_l.bases) : 1 for op=operators]
104-
105-
# Neglect indices covered by composite operators
106-
k = 1
107-
new_idx = Int[]
108-
for i=1:length(operators)
109-
push!(new_idx, indices[k])
110-
ptr_index = filter(p->!(p indices[k:k+op_b_len[i]-1]), 1:N)
111-
operators[i].basis_l == ptrace(basis_l, ptr_index) || throw(bases.IncompatibleBases())
112-
operators[i].basis_r == ptrace(basis_r, ptr_index) || throw(bases.IncompatibleBases())
113-
k += op_b_len[i]
114-
end
115-
# Recursively lower index to fit length
116-
for i=1:length(operators)-1
117-
new_idx[i+1:end] .-= (op_b_len[i] - 1)
118-
end
125+
@assert length(indices) == length(operators)
119126

120-
n = N-sum(op_b_len) + length(operators)
121-
# Assign operators according to their position
122-
out = Array{AbstractOperator,1}(undef, n)
123-
for i=1:length(operators)
124-
out[new_idx[i]] = operators[i]
127+
idxop_sb = [x for x in zip(indices, operators) if typeof(x[1]) <: Int64]
128+
indices_sb = [x[1] for x in idxop_sb]
129+
ops_sb = [x[2] for x in idxop_sb]
130+
131+
for (idxsb, opsb) in zip(indices_sb, ops_sb)
132+
@assert (opsb.basis_l == basis_l.bases[idxsb]) || throw(bases.IncompatibleBases())
133+
@assert (opsb.basis_r == basis_r.bases[idxsb]) || throw(bases.IncompatibleBases())
125134
end
126-
# Find positions and assign identities
127-
id_idx = filter(p->!(p indices), 1:N)
128-
new_id_idx = filter(p->!(p new_idx), 1:n)
129-
@assert length(id_idx) == length(new_id_idx)
130-
for i=1:length(id_idx)
131-
out[new_id_idx[i]] = identityoperator(T, basis_l.bases[id_idx[i]], basis_r.bases[id_idx[i]])
135+
136+
embed_op = tensor([i indices_sb ? ops_sb[indexin(i, indices_sb)[1]] : identityoperator(T, basis_l.bases[i], basis_r.bases[i]) for i=1:N]...)
137+
138+
idxop_comp = [x for x in zip(indices, operators) if typeof(x[1]) <: Array]
139+
140+
for (idxs, op) in idxop_comp
141+
embed_op *= embed(basis_l, basis_r, idxs, op)
132142
end
133143

134-
return tensor(out...)
144+
return embed_op
135145
end
136146

137147

@@ -145,16 +155,16 @@ function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
145155
N = length(basis_l.bases)
146156
@assert length(basis_r.bases) == N
147157

148-
@assert reduce(tensor, basis_l.bases[indices]) == op.basis_l
149-
@assert reduce(tensor, basis_r.bases[indices]) == op.basis_r
158+
@assert reduce(tensor, basis_l.bases[indices]) == op.basis_l || throw(bases.IncompatibleBases())
159+
@assert reduce(tensor, basis_r.bases[indices]) == op.basis_r || throw(bases.IncompatibleBases())
150160

151161
index_order = [idx for idx in 1:length(basis_l.bases) if idx indices]
152162
all_operators = AbstractOperator[identityoperator(T, basis_l.bases[i], basis_r.bases[i]) for i in index_order]
153163

154-
for idx in indices[end:-1:1]
164+
for idx in indices
155165
pushfirst!(index_order, idx)
156166
end
157-
pushfirst!(all_operators, op)
167+
push!(all_operators, op)
158168

159169
sortedindices.check_indices(N, indices)
160170

@@ -165,8 +175,8 @@ function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
165175
unpermuted_op = copy(permuted_op)
166176

167177
# Create the correctly ordered basis.
168-
unpermuted_op.basis_l = permutesystems(permuted_op.basis_l, index_order)
169-
unpermuted_op.basis_r = permutesystems(permuted_op.basis_r, index_order)
178+
unpermuted_op.basis_l = basis_l
179+
unpermuted_op.basis_r = basis_r
170180

171181
# Reorient the matrix to act in the correctly ordered basis.
172182
# Get the dimensions necessary for index permuting.
@@ -181,8 +191,8 @@ function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
181191
expand_dims_r = dims_r[expand_order]
182192

183193
# Prepare the permutation to the correctly ordered basis.
184-
perm_order_l = [indexin(idx, length(dims_l):-1:1)[1] for idx in expand_order]
185-
perm_order_r = [indexin(idx, length(dims_r):-1:1)[1] for idx in expand_order]
194+
perm_order_l = [indexin(idx, expand_order)[1] for idx in 1:length(dims_l)]
195+
perm_order_r = [indexin(idx, expand_order)[1] for idx in 1:length(dims_r)]
186196

187197
# Perform the index expansion, the permutation, and the index collapse.
188198
M = (reshape(permuted_op.data, tuple([expand_dims_l; expand_dims_r]...)) |>
@@ -195,7 +205,7 @@ function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
195205
end
196206
embed(basis_l::CompositeBasis, basis_r::CompositeBasis, index::Int, op::AbstractOperator) = embed(basis_l, basis_r, Int[index], [op])
197207
embed(basis::CompositeBasis, index::Int, op::AbstractOperator) = embed(basis, basis, Int[index], [op])
198-
embed(basis::CompositeBasis, indices::Vector{Int}, operators::Vector{T}) where {T<:AbstractOperator} = embed(basis, basis, indices, operators)
208+
embed(basis::CompositeBasis, indices::Vector, operators::Vector{T}) where {T<:AbstractOperator} = embed(basis, basis, indices, operators)
199209
embed(basis::CompositeBasis, indices::Vector{Int}, op::AbstractOperator) = embed(basis, basis, indices, op)
200210

201211
"""

test/test_operators.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,19 @@ op_test3 = test_operators(b1 ⊗ b2, b2 ⊗ b1, randoperator(b, b).data)
6565
@test_throws bases.IncompatibleBases embed(b1b2, [2], [op1])
6666

6767
b_comp = bb
68-
@test embed(b_comp, [1,3,4], [op1,op]) == dense(op1 one(b2) op)
69-
@test embed(b_comp, [1,2,4], [op,op2]) == dense(op one(b1) op2)
70-
@test_throws bases.IncompatibleBases embed(b_comp, [1,2,3], [op,op2])
71-
@test_throws bases.IncompatibleBases embed(b_comp, [1,3,4], [op,op2])
68+
@test embed(b_comp, [1,[3,4]], [op1,op]) == dense(op1 one(b2) op)
69+
@test embed(b_comp, [[1,2],4], [op,op2]) == dense(op one(b1) op2)
70+
@test_throws bases.IncompatibleBases embed(b_comp, [[1,2],3], [op,op2])
71+
@test_throws bases.IncompatibleBases embed(b_comp, [[1,3],4], [op,op2])
7272

7373
b_comp = b_compb_comp
74-
@test embed(b_comp, [1,3,4,7], [op1,op,op1]) == dense(tensor([op1,one(b2),op,one(b1),one(b2),op1,one(b2)]...))
74+
@test real(sum((abs.(dense(tensor([op1,one(b2),op,one(b1),one(b2),op1,one(b2)]...))-embed(b_comp, [1,[3,4],7], [op1,op,op1]))).data)) < 1e-10
75+
76+
b8 = b2b2b2
77+
cnot = [1 0 0 0; 0 1 0 0; 0 0 0 1; 0 0 1 0]
78+
op_cnot = DenseOperator(b2b2, cnot)
79+
OP_cnot = embed(b8, [1,3], op_cnot)
80+
@assert ptrace(OP_cnot, [2])/2. == op_cnot
7581

7682
@test_throws ErrorException QuantumOptics.operators.gemm!()
7783
@test_throws ErrorException QuantumOptics.operators.gemv!()

0 commit comments

Comments
 (0)