Skip to content

Commit 48b3457

Browse files
authored
Merge pull request #122 from MSRudolph/dev
v0.5.2
2 parents 2d0a7fb + a793ac2 commit 48b3457

File tree

7 files changed

+312
-240
lines changed

7 files changed

+312
-240
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "PauliPropagation"
22
uuid = "293282d5-3c99-4fb6-92d0-fd3280a19750"
33
authors = ["Manuel S. Rudolph"]
4-
version = "0.5.1"
4+
version = "0.5.2"
55

66
[deps]
77
BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1"

examples/3-utility-example.ipynb

Lines changed: 163 additions & 167 deletions
Large diffs are not rendered by default.

src/PauliAlgebra/bitoperations.jl

Lines changed: 28 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -138,93 +138,55 @@ end
138138
# XOR between two Pauli different non-identity strings gives the third one. Ignores signs or any coefficient.
139139
_bitpaulimultiply(pstr1::PauliStringType, pstr2::PauliStringType) = pstr1 pstr2
140140

141-
142-
143141
# Shift to the right and truncate the first encoded Pauli string. Just a utility function.
144142
_paulishiftright(pstr::PauliStringType) = pstr >> 2
145143

144+
# Computing bit shift index from Pauli site index
145+
# this is is the amount we need to shift to get to the target Pauli
146+
_bitshiftfromsiteindex(siteindex::Integer) = 2 * (siteindex - 1)
147+
148+
# a site is represented by two bits
149+
# using Bits.jl implementation of mask
150+
_paulimask(::Type{T}, n_sites) where T = mask(T, 2 * n_sites)
146151

152+
_pauliwindowmask(::Type{T}, index1::Integer, index2::Integer) where T = _paulimask(T, index2 - index1 + 1) << _bitshiftfromsiteindex(index1)
147153

148154
# This function extracts the Pauli at position `index` from the integer Pauli string.
149155
function _getpaulibits(pstr::PauliStringType, index::Integer)
150-
# we need to shift the integer by 2 * (index - 1), then the first two bits are target Pauli
151-
bitindex = 2 * (index - 1)
152-
153-
# shift to the right
154-
shifted_pstr = (pstr >> bitindex)
155-
156-
# AND with 3 (00000011) to get the first two bits
157-
return shifted_pstr & typeof(pstr)(3)
156+
return _getpaulibits(pstr, index, index)
158157
end
159158

159+
# This function extracts the Pauli from `index1` to `index2`.
160+
function _getpaulibits(pstr::PauliStringType, index1::Integer, index2::Integer)
161+
T = typeof(pstr)
160162

161-
# Gets the bit at index `bitindex` in the integer Pauli string.
162-
function _getbit(pauli::Integer, bitindex::Integer)
163-
# return integer with ...000[bit].
163+
bitindex = _bitshiftfromsiteindex(index1)
164164

165-
# shift by bitindex
166-
shifted_pauli = (pauli >> bitindex)
165+
# shift to the right
166+
shifted_pstr = (pstr >> bitindex)
167167

168-
# AND with 1 to get first bit
169-
return shifted_pauli & typeof(pauli)(1)
168+
# creates all 1s mask of length n bits
169+
# AND to get the first n bits
170+
return shifted_pstr & _paulimask(T, index2 - index1 + 1)
170171
end
171172

172173

173174
# This function sets the Pauli at position `index` in the integer Pauli string to `target_pauli`.
174175
function _setpaulibits(pstr::PauliStringType, target_pauli::PauliType, index::Integer)
175-
# we need to shift the integer by 2 * (index - 1), then the first two bits are target Pauli
176-
bitindex = 2 * (index - 1)
177-
178-
# read bits of the pauli
179-
b1 = _getbit(target_pauli, 0)
180-
b2 = _getbit(target_pauli, 1)
181-
182-
# insert them into the pstr
183-
pstr = _setbit(pstr, b1, bitindex)
184-
pstr = _setbit(pstr, b2, bitindex + 1)
185-
return pstr
176+
return _setpaulibits(pstr, target_pauli, index, index)
186177
end
187178

188179

189-
# Sets a bit at index `bitindex` in the integer Pauli string to the value of `target_bit`.
190-
function _setbit(pstr::PauliStringType, target_bit::Integer, bitindex::Integer)
191-
# set bit at bitindex to bit
192-
193-
if target_bit == true # set to one
194-
pstr = _setbittoone(pstr, bitindex)
195-
else
196-
pstr = _setbittozero(pstr, bitindex)
197-
end
198-
return pstr
199-
end
200-
201-
202-
# Sets a bit at index `bitindex` in the integer Pauli string to 1.
203-
function _setbittoone(pstr::Integer, bitindex::Integer)
204-
# set bit at bitindex to 1
205-
206-
# shift ...00100... to bitindex
207-
shifted_onebit = (typeof(pstr)(1) << bitindex)
208-
209-
# OR with pauli string to make sure that that bit is 1
210-
return pstr | shifted_onebit
211-
end
212-
213-
214-
215-
# Sets a bit at index `bitindex` in the integer Pauli string to 0.
216-
function _setbittozero(pstr::Integer, bitindex::Integer)
217-
# set bit at bitindex to 0
180+
# This function sets the Pauli from `index1` to `index2` to `target_pstr`.
181+
function _setpaulibits(pstr::PauliStringType, target_pstr::PauliStringType, index1::Integer, index2::Integer)
182+
T = typeof(pstr)
218183

219-
# flip all bits
220-
pstr = ~pstr
184+
bitindex = _bitshiftfromsiteindex(index1)
221185

222-
# set target bit to one
223-
pstr = _setbittoone(pstr, bitindex)
186+
window_mask = _pauliwindowmask(T, index1, index2)
224187

225-
# flip all bits back, only the target bit is 0
226-
pstr = ~pstr
227-
return pstr
188+
# set bits to target pstr
189+
return (pstr & ~window_mask) | (T(target_pstr) << bitindex)
228190
end
229191

230192

@@ -234,10 +196,10 @@ end
234196

235197
# length is the number of bits in the integer
236198
n_bits = min(bitsize(pstr), 2_048) # for max 1024 qubits.
237-
mask = zero(pstr)
199+
mask = zero(T)
238200
for ii in 0:(n_bits-1)
239201
if ii % 2 == 0
240-
mask = _setbittoone(mask, ii)
202+
mask = mask | (T(1) << ii)
241203
end
242204
end
243205
return mask

src/PauliAlgebra/utils.jl

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,32 @@ end
178178
Gets the Paulis on indices `qinds` of a `pstr` in the integer representation.
179179
"""
180180
function getpauli(pstr::PauliStringType, qinds)
181-
new_pstr = identitylike(pstr)
182-
# Get the Paulis on the indices `qinds`
183-
for (ii, qind) in enumerate(qinds)
184-
pauli = getpauli(pstr, qind)
185-
new_pstr = setpauli(new_pstr, pauli, ii)
181+
pstr_new = zero(pstr)
182+
for (i, qind) in enumerate(qinds)
183+
pair = _getpaulibits(pstr, qind) # get two bits for pauli at qind
184+
pstr_new |= (pair << (2 * (i - 1))) # append pair using bitwise OR at 2i
186185
end
187-
return new_pstr
186+
return pstr_new
187+
end
188+
189+
190+
"""
191+
getpauli(pstr::PauliStringType, qind1::Int, qind2::Int)
192+
193+
Gets the Paulis from `qind1` to `qind2` of a `pstr` in the integer representation.
194+
This function is useful for extracting a continuous sub-PauliString.
195+
"""
196+
function getpauli(pstr::PauliStringType, qind1::Int, qind2::Int)
197+
# Get the Paulis on the indices from `qind1` to `qind2`
198+
if qind1 > qind2
199+
throw(ArgumentError("`qind1` should be less than or equal to `qind2`. Got `qind1=$qind1` and `qind2=$qind2`."))
200+
end
201+
202+
return _getpaulibits(pstr, qind1, qind2)
188203

189204
end
190205

206+
191207
"""
192208
setpauli(pstr::PauliStringType, target_pauli::PauliType, index::Integer)
193209
@@ -198,6 +214,24 @@ function setpauli(pstr::PauliStringType, target_pauli::PauliType, index::Integer
198214
return _setpaulibits(pstr, target_pauli, index)
199215
end
200216

217+
218+
"""
219+
setpauli(pstr::PauliStringType, target_paulis::PauliStringType, index1::Integer, index2::Integer)
220+
221+
Sets the Paulis from `index1` to `index2` of an integer Pauli string to `target_paulis`.
222+
"""
223+
function setpauli(pstr::PauliStringType, target_paulis::PauliStringType, index1::Integer, index2::Integer)
224+
225+
if index1 > index2
226+
throw(ArgumentError("`index1` should be less than or equal to `index2`. Got `index1=$index1` and `index2=$index2`."))
227+
end
228+
229+
# TODO: check that `target_paulis` is of the correct length
230+
231+
return _setpaulibits(pstr, target_paulis, index1, index2)
232+
end
233+
234+
201235
"""
202236
setpauli(pstr::PauliStringType, target_pauli::Symbol, index::Integer)
203237
@@ -291,3 +325,39 @@ function _getprettystr(psum::Dict, nqubits::Int; max_lines=20)
291325
return str
292326

293327
end
328+
329+
330+
# Conversion of PauliString and PauliSum to different coefficient types
331+
function Base.convert(::Type{PauliString{TT1,CT1}}, pstr::PauliString{TT2,CT2}) where {TT1,TT2,CT1,CT2}
332+
if TT1 != TT2
333+
throw(ArgumentError("Cannot change term type from $TT2 to $TT1"))
334+
end
335+
return PauliString(pstr.nqubits, convert(TT1, pstr.term), convert(CT1, pstr.coeff))
336+
end
337+
338+
function Base.convert(::Type{PauliSum{TT1,CT1}}, psum::PauliSum{TT2,CT2}) where {TT1,TT2,CT1,CT2}
339+
if TT1 != TT2
340+
throw(ArgumentError("Cannot change term type from $TT2 to $TT1"))
341+
end
342+
return PauliSum(pstr.nqubits, convert(Dict{TT1,CT1}, psum.terms))
343+
344+
end
345+
346+
# # Examples
347+
# ```julia
348+
# convertcoefftype(Float64, PauliString(2, :X, 1, 1+0im))
349+
# ```
350+
# """
351+
function convertcoefftype(::Type{CT1}, pstr::PauliString{TT,CT2}) where {TT,CT1,CT2}
352+
return PauliString(pstr.nqubits, pstr.term, convert(CT1, pstr.coeff))
353+
end
354+
355+
# # Examples
356+
# ```julia
357+
# psum = PauliSum(PauliString(2, :X, 1, 1+0im))
358+
# convertcoefftype(Float64, psum)
359+
# ```
360+
# """
361+
function convertcoefftype(::Type{CT1}, psum::PauliSum{TT,CT2}) where {TT,CT1,CT2}
362+
return PauliSum(psum.nqubits, convert(Dict{TT,CT1}, psum.terms))
363+
end

src/PauliPropagation.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export
4040
pauliprod,
4141
commutes,
4242
commutator,
43+
convertcoefftype,
4344
getinttype
4445

4546
include("PauliTransferMatrix/PauliTransferMatrix.jl")

src/stateoverlap.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ If |x><x| is a computational basis state, it we compute Tr[psum * |x><x|] = <x|p
6666
For example, `overlapwithcomputational(psum, [1,2,4])` returns the overlap with `|1101000...>`.
6767
"""
6868
function overlapwithcomputational(psum::PauliSum, onebitinds)
69+
if length(psum) == 0
70+
return 0.0
71+
end
72+
6973
val = zero(numcoefftype(psum))
7074
for (pstr, coeff) in psum
7175
val += tonumber(coeff) * _calcsignwithones(pstr, onebitinds)

test/test_paulialgebra_utils.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,45 @@ end
112112

113113
end
114114

115+
116+
@testset "Get Pauli for a range of indices" begin
117+
nq = 12
118+
pstr = PauliString(nq, [:X, :Z], [3, 9])
119+
120+
@test getpauli(pstr.term, range(2, 10)) == getpauli(pstr.term, range(2, 10))
121+
@test getpauli(pstr.term, range(2, 10)) == getpauli(pstr.term, 2, 10)
122+
end
123+
124+
@testset "Set Pauli Continuous" begin
125+
nq = 12
126+
pstr = PauliString(nq, [:X, :Z], [3, 9])
127+
target_pstr = PauliString(nq, [:Y, :X], [4, 5])
128+
@test setpauli(pstr.term, target_pstr.term, 2, 10) == setpauli(pstr.term, target_pstr.term, range(2, 10))
129+
end
130+
131+
132+
@testset "Convert PauliString/PauliSum coeff types" begin
133+
134+
@testset "PauliString" begin
135+
pstr = PauliString(2, :X, 1, 1 + 0im)
136+
@test convertcoefftype(Float64, pstr) == PauliString(2, :X, 1, 1.)
137+
138+
pstr = PauliString(2, [:X, :Y], [1, 2], 1)
139+
@test convertcoefftype(ComplexF64, pstr) == PauliString(2, [:X, :Y], [1, 2], 1 + 0im)
140+
end
141+
142+
@testset "PauliSum" begin
143+
psum = PauliSum(ComplexF64, 2)
144+
add!(psum, [:X, :Y], [1, 2], 2 + 0im)
145+
add!(psum, :Z, 1, 1 + 0im)
146+
expected_psum = PauliSum(2)
147+
add!(expected_psum, [:X, :Y], [1, 2], 2)
148+
add!(expected_psum, :Z, 1, 1)
149+
@test convertcoefftype(Float64, psum) == expected_psum
150+
end
151+
152+
end
153+
115154
@testset "Set Pauli for `PauliString` type" begin
116155

117156
# nqubits = 4

0 commit comments

Comments
 (0)