@@ -6,6 +6,7 @@ export AbstractOperator, DataOperator, length, basis, dagger, ishermitian, tenso
66
77import Base: == , + , - , * , / , ^ , length, one, exp, conj, conj!, transpose
88import LinearAlgebra: tr, ishermitian
9+ import SparseArrays: sparse
910import .. bases: basis, tensor, ptrace, permutesystems,
1011 samebases, check_samebases, multiplicable
1112import .. states: dagger, normalize, normalize!
@@ -94,16 +95,98 @@ tensor(operators::AbstractOperator...) = reduce(tensor, operators)
9495Tensor product of operators where missing indices are filled up with identity operators.
9596"""
9697function embed (basis_l:: CompositeBasis , basis_r:: CompositeBasis ,
97- indices:: Vector{Int} , operators:: Vector{T} ) where T<: AbstractOperator
98+ indices:: Vector , operators:: Vector{T} ) where T<: AbstractOperator
99+
100+ @assert sortedindices. check_embed_indices (indices)
101+
98102 N = length (basis_l. bases)
99103 @assert length (basis_r. bases) == N
100104 @assert length (indices) == length (operators)
105+
106+ # Embed all single-subspace operators.
107+ idxop_sb = [x for x in zip (indices, operators) if typeof (x[1 ]) <: Int ]
108+ indices_sb = [x[1 ] for x in idxop_sb]
109+ ops_sb = [x[2 ] for x in idxop_sb]
110+
111+ for (idxsb, opsb) in zip (indices_sb, ops_sb)
112+ (opsb. basis_l == basis_l. bases[idxsb]) || throw (bases. IncompatibleBases ())
113+ (opsb. basis_r == basis_r. bases[idxsb]) || throw (bases. IncompatibleBases ())
114+ end
115+
116+ 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]. .. )
117+
118+ # Embed all joint-subspace operators.
119+ idxop_comp = [x for x in zip (indices, operators) if typeof (x[1 ]) <: Array ]
120+ for (idxs, op) in idxop_comp
121+ embed_op *= embed (basis_l, basis_r, idxs, op)
122+ end
123+
124+ return embed_op
125+ end
126+
127+
128+ """
129+ embed(basis1[, basis2], indices::Vector, operators::Vector)
130+
131+ Embed operator acting on a joint Hilbert space where missing indices are filled up with identity operators.
132+ """
133+ function embed (basis_l:: CompositeBasis , basis_r:: CompositeBasis ,
134+ indices:: Vector{Int} , op:: T ) where T<: AbstractOperator
135+ N = length (basis_l. bases)
136+ @assert length (basis_r. bases) == N
137+
138+ reduce (tensor, basis_l. bases[indices]) == op. basis_l || throw (bases. IncompatibleBases ())
139+ reduce (tensor, basis_r. bases[indices]) == op. basis_r || throw (bases. IncompatibleBases ())
140+
141+ index_order = [idx for idx in 1 : length (basis_l. bases) if idx ∉ indices]
142+ all_operators = AbstractOperator[identityoperator (T, basis_l. bases[i], basis_r. bases[i]) for i in index_order]
143+
144+ for idx in indices
145+ pushfirst! (index_order, idx)
146+ end
147+ push! (all_operators, op)
148+
101149 sortedindices. check_indices (N, indices)
102- tensor ([i ∈ indices ? operators[indexin (i, indices)[1 ]] : identityoperator (T, basis_l. bases[i], basis_r. bases[i]) for i= 1 : N]. .. )
150+
151+ # Create the operator.
152+ permuted_op = tensor (all_operators... )
153+
154+ # Create a copy to fill with correctly-ordered objects (basis and data).
155+ unpermuted_op = copy (permuted_op)
156+
157+ # Create the correctly ordered basis.
158+ unpermuted_op. basis_l = basis_l
159+ unpermuted_op. basis_r = basis_r
160+
161+ # Reorient the matrix to act in the correctly ordered basis.
162+ # Get the dimensions necessary for index permuting.
163+ dims_l = [b. shape[1 ] for b in basis_l. bases]
164+ dims_r = [b. shape[1 ] for b in basis_r. bases]
165+
166+ # Get the order of indices to use in the first reshape. Julia indices go in
167+ # reverse order.
168+ expand_order = index_order[end : - 1 : 1 ]
169+ # Get the dimensions associated with those indices.
170+ expand_dims_l = dims_l[expand_order]
171+ expand_dims_r = dims_r[expand_order]
172+
173+ # Prepare the permutation to the correctly ordered basis.
174+ perm_order_l = [indexin (idx, expand_order)[1 ] for idx in 1 : length (dims_l)]
175+ perm_order_r = [indexin (idx, expand_order)[1 ] for idx in 1 : length (dims_r)]
176+
177+ # Perform the index expansion, the permutation, and the index collapse.
178+ M = (reshape (permuted_op. data, tuple ([expand_dims_l; expand_dims_r]. .. )) |>
179+ x -> permutedims (x, [perm_order_l; perm_order_r .+ length (dims_l)]) |>
180+ x -> sparse (reshape (x, (prod (dims_l), prod (dims_r)))))
181+
182+ unpermuted_op. data = M
183+
184+ return unpermuted_op
103185end
104186embed (basis_l:: CompositeBasis , basis_r:: CompositeBasis , index:: Int , op:: AbstractOperator ) = embed (basis_l, basis_r, Int[index], [op])
105187embed (basis:: CompositeBasis , index:: Int , op:: AbstractOperator ) = embed (basis, basis, Int[index], [op])
106- embed (basis:: CompositeBasis , indices:: Vector{Int} , operators:: Vector{T} ) where {T<: AbstractOperator } = embed (basis, basis, indices, operators)
188+ embed (basis:: CompositeBasis , indices:: Vector , operators:: Vector{T} ) where {T<: AbstractOperator } = embed (basis, basis, indices, operators)
189+ embed (basis:: CompositeBasis , indices:: Vector{Int} , op:: AbstractOperator ) = embed (basis, basis, indices, op)
107190
108191"""
109192 embed(basis1[, basis2], operators::Dict)
0 commit comments