diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml new file mode 100644 index 00000000..d77d3a0c --- /dev/null +++ b/.github/workflows/TagBot.yml @@ -0,0 +1,11 @@ +name: TagBot +on: + schedule: + - cron: 0 * * * * +jobs: + TagBot: + runs-on: ubuntu-latest + steps: + - uses: JuliaRegistries/TagBot@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.travis.yml b/.travis.yml index 87547890..9729bc28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,17 +2,15 @@ language: julia os: - osx - linux + - windows julia: - - 0.6 + - 1 - nightly matrix: allow_failures: - julia: nightly notifications: email: false -script: - - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi - - julia -e 'Pkg.clone(pwd()); Pkg.build("QuantumOptics"); Pkg.test("QuantumOptics"; coverage=true)'; after_success: - - julia -e 'cd(Pkg.dir("QuantumOptics")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' - - julia -e 'cd(Pkg.dir("QuantumOptics")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'; + - julia -e 'import Pkg; cd(Pkg.dir("QuantumOptics")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' + - julia -e 'import Pkg; cd(Pkg.dir("QuantumOptics")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'; diff --git a/Project.toml b/Project.toml new file mode 100644 index 00000000..6795672e --- /dev/null +++ b/Project.toml @@ -0,0 +1,38 @@ +name = "QuantumOptics" +uuid = "6e0679c1-51ea-5a7c-ac74-d61b76210b0c" +version = "v0.7.1" + +[deps] +QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678" +Arpack = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97" +DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def" +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0" +WignerSymbols = "9f57e263-0b3d-5e2e-b1be-24f2bb48858b" + +[compat] +QuantumOpticsBase = "0.1" +Arpack = "0.4" +DiffEqCallbacks = "2" +FFTW = "1.2" +OrdinaryDiffEq = "5" +RecursiveArrayTools = "2" +Reexport = "0.2" +StochasticDiffEq = "6" +WignerSymbols = "1" +julia = "1" + +[extras] +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["LinearAlgebra", "SparseArrays", "Random", "Test"] diff --git a/README.md b/README.md index 84222fe8..9d53eceb 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,8 @@ More information, documentation and examples can be found on our website http://qojulia.org. -### Development status - **Latest release:** - * [![Status of latest release on julia 0.6][pkg-0.6-img]][pkg-0.6-url] + * Version: [![Latest version tag][version-img]][version-url] * Test coverage: [![Test coverage status on coveralls][coveralls-img]][coveralls-url] [![Test coverage status on codecov][codecov-img]][codecov-url] @@ -20,7 +18,7 @@ More information, documentation and examples can be found on our website http:// ### Project structure -The content associated with **QuantumOptics.jl** is distributed over several repositories under the [qojulia] organization on github: +The source content associated with **QuantumOptics.jl** is distributed over several repositories under the [qojulia] organization on github: * The main code: https://github.com/qojulia/QuantumOptics.jl * Documentation: https://github.com/qojulia/QuantumOptics.jl-documentation @@ -31,7 +29,7 @@ The content associated with **QuantumOptics.jl** is distributed over several rep ### Questions & Contributions -Contributions of any kind are always welcome! Be it as questions, ideas for new features, bug reports or, our favorite case, sending pull requests. +If you have any questions or need help, hop on our [gitter channel](https://gitter.im/QuantumOptics-jl/Lobby?source=orgpage) and ask away. Also, contributions of any kind are always welcome! Be it as ideas for new features, bug reports or, our favorite case, sending pull requests. ### Citing @@ -62,3 +60,6 @@ If you like **QuantumOptics.jl**, we would appreciate it if you starred the repo [docs-url]: https://qojulia.org/documentation/ [docs-img]: https://img.shields.io/badge/docs-stable-blue.svg + +[version-url]: https://github.com/qojulia/QuantumOptics.jl/releases +[version-img]: https://img.shields.io/github/release/qojulia/QuantumOptics.jl.svg diff --git a/REQUIRE b/REQUIRE deleted file mode 100644 index cc8b7c29..00000000 --- a/REQUIRE +++ /dev/null @@ -1,7 +0,0 @@ -julia 0.6 -Compat 0.64.0 -OrdinaryDiffEq 3.19.1 -DiffEqCallbacks 1.1 -StochasticDiffEq 4.4.5 -RecursiveArrayTools -WignerSymbols 0.1.1 diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 3d66d031..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,40 +0,0 @@ -environment: - matrix: - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" - -matrix: - allow_failures: - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" - -branches: - only: - - master - - /release-.*/ - -notifications: - - provider: Email - on_build_success: false - on_build_failure: false - on_build_status_changed: false - -install: - - ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" -# Download most recent Julia Windows binary - - ps: (new-object net.webclient).DownloadFile( - $env:JULIA_URL, - "C:\projects\julia-binary.exe") -# Run installer silently, output to C:\projects\julia - - C:\projects\julia-binary.exe /S /D=C:\projects\julia - -build_script: -# Need to convert from shallow to complete for Pkg.clone to work - - IF EXIST .git\shallow (git fetch --unshallow) - - C:\projects\julia\bin\julia -e "versioninfo(); - Pkg.clone(pwd(), \"QuantumOptics\"); Pkg.build(\"QuantumOptics\")" - -test_script: - - C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"QuantumOptics\")" diff --git a/src/QuantumOptics.jl b/src/QuantumOptics.jl index c5d0dc1c..1b6e13ba 100644 --- a/src/QuantumOptics.jl +++ b/src/QuantumOptics.jl @@ -1,110 +1,40 @@ -__precompile__() - module QuantumOptics -export bases, Basis, GenericBasis, CompositeBasis, basis, - tensor, ⊗, permutesystems, - states, StateVector, Bra, Ket, basisstate, - dagger, normalize, normalize!, - operators, Operator, expect, variance, identityoperator, ptrace, embed, - operators_dense, DenseOperator, projector, dm, - operators_sparse, SparseOperator, diagonaloperator, - operators_lazysum, LazySum, - operators_lazyproduct, LazyProduct, - operators_lazytensor, LazyTensor, - superoperators, SuperOperator, DenseSuperOperator, SparseSuperOperator, - spre, spost, liouvillian, - fock, FockBasis, number, destroy, create, - fockstate, coherentstate, displace, - randstate, randoperator, thermalstate, coherentthermalstate, phase_average, passive_state, - spin, SpinBasis, sigmax, sigmay, sigmaz, sigmap, sigmam, spinup, spindown, - subspace, SubspaceBasis, projector, - particle, PositionBasis, MomentumBasis, samplepoints, gaussianstate, - position, momentum, potentialoperator, transform, - nlevel, NLevelBasis, transition, nlevelstate, - manybody, ManyBodyBasis, fermionstates, bosonstates, - manybodyoperator, onebodyexpect, occupation, - phasespace, qfunc, wigner, coherentspinstate, qfuncsu2, wignersu2, ylm, - metrics, tracenorm, tracenorm_h, tracenorm_nh, - tracedistance, tracedistance_h, tracedistance_nh, - entropy_vn, fidelity, ptranspose, PPT, - negativity, logarithmic_negativity, - spectralanalysis, eigenstates, eigenenergies, simdiag, - timeevolution, diagonaljumps, +using Reexport +@reexport using QuantumOpticsBase +using SparseArrays, LinearAlgebra + +export qfunc, wigner, coherentspinstate, qfuncsu2, wignersu2, ylm, + eigenstates, eigenenergies, simdiag, + timeevolution, diagonaljumps, @skiptimechecks, steadystate, timecorrelations, semiclassical, stochastic -include("sortedindices.jl") -include("polynomials.jl") -include("bases.jl") -include("states.jl") -include("operators.jl") -include("operators_dense.jl") -include("sparsematrix.jl") -include("operators_sparse.jl") -include("operators_lazysum.jl") -include("operators_lazyproduct.jl") -include("operators_lazytensor.jl") -include("superoperators.jl") -include("spin.jl") -include("fock.jl") -include("state_definitions.jl") -include("subspace.jl") -include("particle.jl") -include("nlevel.jl") -include("manybody.jl") -include("transformations.jl") include("phasespace.jl") -include("metrics.jl") module timeevolution - export diagonaljumps + export diagonaljumps, @skiptimechecks + include("timeevolution_base.jl") include("master.jl") include("schroedinger.jl") include("mcwf.jl") - using .timeevolution_master - using .timeevolution_schroedinger - using .timeevolution_mcwf + include("bloch_redfield_master.jl") end include("steadystate.jl") include("timecorrelations.jl") include("spectralanalysis.jl") include("semiclassical.jl") module stochastic + include("stochastic_base.jl") include("stochastic_definitions.jl") include("stochastic_schroedinger.jl") include("stochastic_master.jl") include("stochastic_semiclassical.jl") - using .stochastic_schroedinger, .stochastic_master, .stochastic_semiclassical - using .stochastic_definitions end -include("printing.jl") -using .bases -using .states -using .operators -using .operators_dense -using .operators_sparse -using .operators_lazysum -using .operators_lazyproduct -using .operators_lazytensor -using .superoperators -using .spin -using .fock -using .state_definitions -using .subspace -using .particle -using .nlevel -using .manybody -using .phasespace using .timeevolution -using .metrics -using .spectralanalysis -using .timecorrelations -using .printing - end # module diff --git a/src/bases.jl b/src/bases.jl deleted file mode 100644 index b3a30a57..00000000 --- a/src/bases.jl +++ /dev/null @@ -1,269 +0,0 @@ -module bases - -export Basis, GenericBasis, CompositeBasis, basis, - tensor, ⊗, ptrace, permutesystems, - IncompatibleBases, - samebases, multiplicable, - check_samebases, check_multiplicable - -import Base: ==, ^ - -using Compat - -""" -Abstract base class for all specialized bases. - -The Basis class is meant to specify a basis of the Hilbert space of the -studied system. Besides basis specific information all subclasses must -implement a shape variable which indicates the dimension of the used -Hilbert space. For a spin-1/2 Hilbert space this would be the -vector `Int[2]`. A system composed of two spins would then have a -shape vector `Int[2 2]`. - -Composite systems can be defined with help of the [`CompositeBasis`](@ref) -class. -""" -abstract type Basis end - - -==(b1::Basis, b2::Basis) = false - -""" - length(b::Basis) - -Total dimension of the Hilbert space. -""" -Base.length(b::Basis) = prod(b.shape) - -""" - basis(a) - -Return the basis of an object. - -If it's ambiguous, e.g. if an operator has a different left and right basis, -an [`IncompatibleBases`](@ref) error is thrown. -""" -function basis end - - -""" - GenericBasis(N) - -A general purpose basis of dimension N. - -Should only be used rarely since it defeats the purpose of checking that the -bases of state vectors and operators are correct for algebraic operations. -The preferred way is to specify special bases for different systems. -""" -mutable struct GenericBasis <: Basis - shape::Vector{Int} -end - -GenericBasis(N::Int) = GenericBasis(Int[N]) - -==(b1::GenericBasis, b2::GenericBasis) = equal_shape(b1.shape, b2.shape) - - -""" - CompositeBasis(b1, b2...) - -Basis for composite Hilbert spaces. - -Stores the subbases in a vector and creates the shape vector directly -from the shape vectors of these subbases. Instead of creating a CompositeBasis -directly `tensor(b1, b2...)` or `b1 ⊗ b2 ⊗ …` can be used. -""" -mutable struct CompositeBasis <: Basis - shape::Vector{Int} - bases::Vector{Basis} -end -CompositeBasis(bases::Vector{Basis}) = CompositeBasis(Int[prod(b.shape) for b in bases], bases) -CompositeBasis(bases::Basis...) = CompositeBasis(Basis[bases...]) - -==(b1::CompositeBasis, b2::CompositeBasis) = equal_shape(b1.shape, b2.shape) && equal_bases(b1.bases, b2.bases) - -""" - tensor(x, y, z...) - -Tensor product of the given objects. Alternatively, the unicode -symbol ⊗ (\\otimes) can be used. -""" -tensor() = throw(ArgumentError("Tensor function needs at least one argument.")) -tensor(b::Basis) = b - -""" - tensor(x::Basis, y::Basis, z::Basis...) - -Create a [`CompositeBasis`](@ref) from the given bases. - -Any given CompositeBasis is expanded so that the resulting CompositeBasis never -contains another CompositeBasis. -""" -tensor(b1::Basis, b2::Basis) = CompositeBasis(Int[prod(b1.shape); prod(b2.shape)], Basis[b1, b2]) -tensor(b1::CompositeBasis, b2::CompositeBasis) = CompositeBasis(Int[b1.shape; b2.shape], Basis[b1.bases; b2.bases]) -function tensor(b1::CompositeBasis, b2::Basis) - N = length(b1.bases) - shape = Vector{Int}(N+1) - bases = Vector{Basis}(N+1) - for i=1:N - shape[i] = b1.shape[i] - bases[i] = b1.bases[i] - end - shape[end] = prod(b2.shape) - bases[end] = b2 - CompositeBasis(shape, bases) -end -function tensor(b1::Basis, b2::CompositeBasis) - N = length(b2.bases) - shape = Vector{Int}(N+1) - bases = Vector{Basis}(N+1) - for i=1:N - shape[i+1] = b2.shape[i] - bases[i+1] = b2.bases[i] - end - shape[1] = prod(b1.shape) - bases[1] = b1 - CompositeBasis(shape, bases) -end -tensor(bases::Basis...) = reduce(tensor, bases) -⊗ = tensor - -function ^(b::Basis, N::Int) - if N < 1 - throw(ArgumentError("Power of a basis is only defined for positive integers.")) - end - tensor([b for i=1:N]...) -end - -""" - equal_shape(a, b) - -Check if two shape vectors are the same. -""" -function equal_shape(a::Vector{Int}, b::Vector{Int}) - if a === b - return true - end - if length(a) != length(b) - return false - end - for i=1:length(a) - if a[i]!=b[i] - return false - end - end - return true -end - -""" - equal_bases(a, b) - -Check if two subbases vectors are identical. -""" -function equal_bases(a::Vector{T}, b::Vector{T}) where T <: Basis - if a===b - return true - end - for i=1:length(a) - if a[i]!=b[i] - return false - end - end - return true -end - -""" -Exception that should be raised for an illegal algebraic operation. -""" -mutable struct IncompatibleBases <: Exception end - -""" - samebases(a, b) - -Test if two objects have the same bases. -""" -samebases(b1::Basis, b2::Basis) = b1==b2 - -""" - check_samebases(a, b) - -Throw an [`IncompatibleBases`](@ref) error if the objects don't have -the same bases. -""" -function check_samebases(b1, b2) - if !samebases(b1, b2) - throw(IncompatibleBases()) - end -end - - -""" - multiplicable(a, b) - -Check if two objects are multiplicable. -""" -multiplicable(b1::Basis, b2::Basis) = b1==b2 - -function multiplicable(b1::CompositeBasis, b2::CompositeBasis) - if !equal_shape(b1.shape,b2.shape) - return false - end - for i=1:length(b1.shape) - if !multiplicable(b1.bases[i], b2.bases[i]) - return false - end - end - return true -end - -""" - check_multiplicable(a, b) - -Throw an [`IncompatibleBases`](@ref) error if the objects are -not multiplicable. -""" -function check_multiplicable(b1, b2) - if !multiplicable(b1, b2) - throw(IncompatibleBases()) - end -end - - -""" - ptrace(a, indices) - -Partial trace of the given basis, state or operator. - -The `indices` argument, which can be a single integer or a vector of integers, -specifies which subsystems are traced out. The number of indices has to be -smaller than the number of subsystems, i.e. it is not allowed to perform a -full trace. -""" -function ptrace(b::CompositeBasis, indices::Vector{Int}) - J = [i for i in 1:length(b.bases) if i ∉ indices] - if length(J)==0 - throw(ArgumentError("Tracing over all indices is not allowed in ptrace.")) - elseif length(J)==1 - return b.bases[J[1]] - else - return CompositeBasis(b.shape[J], b.bases[J]) - end -end -ptrace(a, index::Int) = ptrace(a, Int[index]) - - -""" - permutesystems(a, perm) - -Change the ordering of the subsystems of the given object. - -For a permutation vector `[2,1,3]` and a given object with basis `[b1, b2, b3]` -this function results in `[b2, b1, b3]`. -""" -function permutesystems(b::CompositeBasis, perm::Vector{Int}) - @assert length(b.bases) == length(perm) - @assert isperm(perm) - CompositeBasis(b.shape[perm], b.bases[perm]) -end - -end # module diff --git a/src/bloch_redfield_master.jl b/src/bloch_redfield_master.jl new file mode 100644 index 00000000..e7ee8695 --- /dev/null +++ b/src/bloch_redfield_master.jl @@ -0,0 +1,240 @@ +""" + bloch_redfield_tensor(H, a_ops; J=[], use_secular=true, secular_cutoff=0.1) + +Create the super-operator for the Bloch-Redfield master equation such that ``\\dot ρ = R ρ`` based on the QuTiP implementation. + +See QuTiP's documentation (http://qutip.org/docs/latest/guide/dynamics/dynamics-bloch-redfield.html) for more information and a brief derivation. + + +# Arguments +* `H`: Hamiltonian. +* `a_ops`: Nested list of [interaction operator, callback function] pairs for the Bloch-Redfield type processes where the callback function describes the environment spectrum for the corresponding interaction operator. + The spectral functions must take the angular frequency as their only argument. +* `J=[]`: Vector containing the jump operators for the Linblad type processes (optional). +* `use_secular=true`: Specify whether or not to use the secular approximation. +* `secular_cutoff=0.1`: Cutoff to allow a degree of partial secularization. Terms are discarded if they are greater than (dw\\_min * secular cutoff) where dw\\_min is the smallest (non-zero) difference between any two eigenenergies of H. + This argument is only taken into account if use_secular=true. +""" +function bloch_redfield_tensor(H::AbstractOperator, a_ops::Array; J=[], use_secular=true, secular_cutoff=0.1) + + # use the eigenbasis + H_evals, transf_mat = eigen(DenseOperator(H).data) + H_ekets = [Ket(H.basis_l, transf_mat[:, i]) for i in 1:length(H_evals)]::Array{Ket{typeof(H.basis_l), Array{Complex{Float64}, 1}}, 1} + + + #Define function for transforming to Hamiltonian eigenbasis + function to_Heb(op) + #Copy oper + oper = copy(op) + #Transform underlying array + oper.data = inv(transf_mat) * oper.data * transf_mat + return oper + end + + N = length(H_evals) + K = length(a_ops) + + #If only Lindblad collapse terms + if K==0 + Heb = to_Heb(H) + L = liouvillian(Heb, to_Heb.(J)) + L = sparse(L) + return L, H_ekets + end + + #Transform interaction operators to Hamiltonian eigenbasis + A = Array{Complex{Float64}}(undef, N, N, K) + for k in 1:K + A[:, :, k] = to_Heb(a_ops[k][1]).data + end + + # Trasition frequencies between eigenstates + W = transpose(H_evals) .- H_evals + + #Array for spectral functions evaluated at transition frequencies + Jw = Array{Complex}(undef, N, N, K) + for k in 1:K + # do explicit loops here + for n in 1:N + for m in 1:N + Jw[m, n, k] = a_ops[k][2](W[n, m]) + end + end + end + + #Calculate secular cutoff scale + W_flat = reshape(W, N*N) + dw_min = minimum(abs.(W_flat[W_flat .!= 0.0])) + + #Pre-calculate mapping between global index I and system indices a,b + Iabs = Array{Int}(undef, N*N, 3) + indices = CartesianIndices((N,N)) + for I in 1:N*N + Iabs[I, 1] = I + Iabs[I, 2:3] = [indices[I].I...] + end + + # Calculate Liouvillian for Lindblad temrs (unitary part + dissipation from J (if given)): + Heb = to_Heb(H) + L = liouvillian(Heb, to_Heb.(J)) + L = sparse(L) + + # Main Bloch-Redfield operators part + rows = Int[] + cols = Int[] + data = Complex[] + Is = view(Iabs, :, 1) + As = view(Iabs, :, 2) + Bs = view(Iabs, :, 3) + + for (I, a, b) in zip(Is, As, Bs) + + if use_secular + Jcds = Int.(zeros(size(Iabs))) + for (row, (I2, a2, b2)) in enumerate(zip(Is, As, Bs)) + if abs.(W[a, b] - W[a2, b2]) < dw_min * secular_cutoff + Jcds[row, :] = [I2 a2 b2] + end + end + Jcds = transpose(Jcds) + Jcds = Jcds[Jcds .!= 0] + Jcds = reshape(Jcds, 3, Int(length(Jcds)/3)) + Jcds = transpose(Jcds) + + Js = view(Jcds, :, 1) + Cs = view(Jcds, :, 2) + Ds = view(Jcds, :, 3) + + else + Js = Is + Cs = As + Ds = Bs + end + + + for (J, c, d) in zip(Js, Cs, Ds) + + elem = 0.5 * sum(view(A, c, a, :) .* view(A, b, d, :) .* (view(Jw, c, a, :) + view(Jw, d, b, :) )) + + if b == d + elem -= 0.5 * sum(transpose(view(A, a, :, :)) .* transpose(view(A, c, :, :)) .* transpose(view(Jw, c, :, :) )) + end + + if a == c + elem -= 0.5 * sum(transpose(view(A, d, :, :)) .* transpose(view(A, b, :, :)) .* transpose(view(Jw, d, :, :) )) + end + + #Add element + if abs(elem) != 0.0 + push!(rows, I) + push!(cols, J) + push!(data, elem) + end + end + end + + + """ + Need to be careful here since sparse function is happy to create rectangular arrays but we dont want this behaviour so need to make square explicitly. + """ + if !any(rows .== N*N) || !any(cols .== N*N) #Check if row or column list has (N*N)th element, if not then add one + push!(rows, N*N) + push!(cols, N*N) + push!(data, 0.0) + end + + R = sparse(rows, cols, data) #Careful with rows/cols ordering... + + #Add Bloch-Redfield part to Linblad Liouvillian calculated earlier + L.data = L.data + R + + return L, H_ekets + +end #Function + + +""" + timeevolution.master_bloch_redfield(tspan, rho0, R, H; ) + +Time-evolution according to a Bloch-Redfield master equation. + + +# Arguments +* `tspan`: Vector specifying the points of time for which output should + be displayed. +* `rho0`: Initial density operator. Can also be a state vector which is + automatically converted into a density operator. +* `H`: Arbitrary operator specifying the Hamiltonian. +* `R`: Bloch-Redfield tensor describing the time-evolution ``\\dot ρ = R ρ`` (see timeevolution.bloch\\_redfield\\_tensor). +* `fout=nothing`: If given, this function `fout(t, rho)` is called every time + an output should be displayed. ATTENTION: The given state rho is not + permanent! It is still in use by the ode solver and therefore must not + be changed. +* `kwargs...`: Further arguments are passed on to the ode solver. +""" +function master_bloch_redfield(tspan::Vector{Float64}, + rho0::T, L::SuperOperator{Tuple{B,B},Tuple{B,B}}, + H::AbstractOperator{B,B}; fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} + + #Prep basis transf + evals, transf_mat = eigen(dense(H).data) + transf_op = DenseOperator(rho0.basis_l, transf_mat) + inv_transf_op = DenseOperator(rho0.basis_l, inv(transf_mat)) + + # rho as Ket and L as DataOperator + basis_comp = rho0.basis_l^2 + rho0_eb = Ket(basis_comp, (inv_transf_op * rho0 * transf_op).data[:]) #Transform to H eb and convert to Ket + L_ = isa(L, DenseSuperOperator) ? DenseOperator(basis_comp, L.data) : SparseOperator(basis_comp, L.data) + + # Derivative function + dmaster_br_(t::Float64, rho::T2, drho::T2) where T2<:Ket = dmaster_br(drho, rho, L_) + + return integrate_br(tspan, dmaster_br_, rho0_eb, transf_op, inv_transf_op, fout; kwargs...) +end +master_bloch_redfield(tspan::Vector{Float64}, psi::Ket, args...; kwargs...) = master_bloch_redfield(tspan::Vector{Float64}, dm(psi), args...; kwargs...) + +# Derivative ∂ₜρ = Lρ +function dmaster_br(drho::T, rho::T, L::DataOperator{B,B}) where {B<:Basis,T<:Ket{B}} + QuantumOpticsBase.gemv!(1.0, L, rho, 0.0, drho) +end + +# Integrate if there is no fout specified +function integrate_br(tspan::Vector{Float64}, dmaster_br::Function, rho::T, + transf_op::T2, inv_transf_op::T2, ::Nothing; + kwargs...) where {T<:Ket,T2<:DenseOperator} + # Pre-allocate for in-place back-transformation from eigenbasis + rho_out = copy(transf_op) + tmp = copy(transf_op) + tmp2 = copy(transf_op) + + # Define fout + function fout(t::Float64, rho::T) + tmp.data[:] = rho.data + QuantumOpticsBase.gemm!(1.0, transf_op, tmp, 0.0, tmp2) + QuantumOpticsBase.gemm!(1.0, tmp2, inv_transf_op, 0.0, rho_out) + return copy(rho_out) + end + + return integrate(tspan, dmaster_br, copy(rho.data), rho, copy(rho), fout; kwargs...) +end + +# Integrate with given fout +function integrate_br(tspan::Vector{Float64}, dmaster_br::Function, rho::T, + transf_op::T2, inv_transf_op::T2, fout::Function; + kwargs...) where {T<:Ket,T2<:DenseOperator} + # Pre-allocate for in-place back-transformation from eigenbasis + rho_out = copy(transf_op) + tmp = copy(transf_op) + tmp2 = copy(transf_op) + + # Perform back-transfomration before calling fout + function fout_(t::Float64, rho::T) + tmp.data[:] = rho.data + QuantumOpticsBase.gemm!(1.0, transf_op, tmp, 0.0, tmp2) + QuantumOpticsBase.gemm!(1.0, tmp2, inv_transf_op, 0.0, rho_out) + return fout(t, rho_out) + end + + return integrate(tspan, dmaster_br, copy(rho.data), rho, copy(rho), fout_; kwargs...) +end diff --git a/src/fock.jl b/src/fock.jl deleted file mode 100644 index 1786c606..00000000 --- a/src/fock.jl +++ /dev/null @@ -1,95 +0,0 @@ -module fock - -export FockBasis, number, destroy, create, displace, fockstate, coherentstate - -import Base: == - -using ..bases, ..states, ..operators, ..operators_dense, ..operators_sparse - - -""" - FockBasis(N) - -Basis for a Fock space where `N` specifies a cutoff, i.e. what the highest -included fock state is. Note that the dimension of this basis then is N+1. -""" -mutable struct FockBasis <: Basis - shape::Vector{Int} - N::Int - function FockBasis(N::Int) - if N < 0 - throw(DimensionMismatch()) - end - new([N+1], N) - end -end - - -==(b1::FockBasis, b2::FockBasis) = b1.N==b2.N - -""" - number(b::FockBasis) - -Number operator for the specified Fock space. -""" -function number(b::FockBasis) - diag = complex.(0.:b.N) - data = spdiagm(diag, 0, b.N+1, b.N+1) - SparseOperator(b, data) -end - -""" - destroy(b::FockBasis) - -Annihilation operator for the specified Fock space. -""" -function destroy(b::FockBasis) - diag = complex.(sqrt.(1.:b.N)) - data = spdiagm(diag, 1, b.N+1, b.N+1) - SparseOperator(b, data) -end - -""" - create(b::FockBasis) - -Creation operator for the specified Fock space. -""" -function create(b::FockBasis) - diag = complex.(sqrt.(1.:b.N)) - data = spdiagm(diag, -1, b.N+1, b.N+1) - SparseOperator(b, data) -end - -""" - displace(b::FockBasis, alpha) - -Displacement operator ``D(α)`` for the specified Fock space. -""" -displace(b::FockBasis, alpha::Number) = expm(full(alpha*create(b) - conj(alpha)*destroy(b))) - -""" - fockstate(b::FockBasis, n) - -Fock state ``|n⟩`` for the specified Fock space. -""" -function fockstate(b::FockBasis, n::Int) - @assert n <= b.N - basisstate(b, n+1) -end - -""" - coherentstate(b::FockBasis, alpha) - -Coherent state ``|α⟩`` for the specified Fock space. -""" -function coherentstate(b::FockBasis, alpha::Number, result=Ket(b, Vector{Complex128}(length(b)))) - alpha = complex(alpha) - data = result.data - data[1] = exp(-abs2(alpha)/2) - @inbounds for n=1:b.N - data[n+1] = data[n]*alpha/sqrt(n) - end - return result -end - -end # module diff --git a/src/manybody.jl b/src/manybody.jl deleted file mode 100644 index c69e6e15..00000000 --- a/src/manybody.jl +++ /dev/null @@ -1,487 +0,0 @@ -module manybody - -export ManyBodyBasis, fermionstates, bosonstates, - manybodyoperator, onebodyexpect, occupation - -import Base: == -import ..states: basisstate -import ..fock: number, create, destroy -import ..nlevel: transition - -using ..bases, ..states, ..operators, ..operators_dense, ..operators_sparse - -""" - ManyBodyBasis(b, occupations) - -Basis for a many body system. - -The basis has to know the associated one-body basis `b` and which occupation states -should be included. The occupations_hash is used to speed up checking if two -many-body bases are equal. -""" -mutable struct ManyBodyBasis <: Basis - shape::Vector{Int} - onebodybasis::Basis - occupations::Vector{Vector{Int}} - occupations_hash::UInt - - function ManyBodyBasis(onebodybasis::Basis, occupations::Vector{Vector{Int}}) - new([length(occupations)], onebodybasis, occupations, hash(occupations)) - end -end - -""" - fermionstates(Nmodes, Nparticles) - fermionstates(b, Nparticles) - -Generate all fermionic occupation states for N-particles in M-modes. -`Nparticles` can be a vector to define a Hilbert space with variable -particle number. -""" -fermionstates(Nmodes::Int, Nparticles::Int) = _distribute_fermions(Nparticles, Nmodes, 1, zeros(Int, Nmodes), Vector{Int}[]) -fermionstates(Nmodes::Int, Nparticles::Vector{Int}) = vcat([fermionstates(Nmodes, N) for N in Nparticles]...) -fermionstates(onebodybasis::Basis, Nparticles) = fermionstates(length(onebodybasis), Nparticles) - -""" - bosonstates(Nmodes, Nparticles) - bosonstates(b, Nparticles) - -Generate all bosonic occupation states for N-particles in M-modes. -`Nparticles` can be a vector to define a Hilbert space with variable -particle number. -""" -bosonstates(Nmodes::Int, Nparticles::Int) = _distribute_bosons(Nparticles, Nmodes, 1, zeros(Int, Nmodes), Vector{Int}[]) -bosonstates(Nmodes::Int, Nparticles::Vector{Int}) = vcat([bosonstates(Nmodes, N) for N in Nparticles]...) -bosonstates(onebodybasis::Basis, Nparticles) = bosonstates(length(onebodybasis), Nparticles) - -==(b1::ManyBodyBasis, b2::ManyBodyBasis) = b1.occupations_hash==b2.occupations_hash && b1.onebodybasis==b2.onebodybasis - -""" - basisstate(b::ManyBodyBasis, occupation::Vector{Int}) - -Return a ket state where the system is in the state specified by the given -occupation numbers. -""" -function basisstate(basis::ManyBodyBasis, occupation::Vector{Int}) - index = findfirst(basis.occupations, occupation) - if index == 0 - throw(ArgumentError("Occupation not included in many-body basis.")) - end - basisstate(basis, index) -end - -function isnonzero(occ1, occ2, index) - for i=1:length(occ1) - if i == index - if occ1[i] != occ2[i] + 1 - return false - end - else - if occ1[i] != occ2[i] - return false - end - end - end - true -end - -""" - create(b::ManyBodyBasis, index) - -Creation operator for the i-th mode of the many-body basis `b`. -""" -function create(b::ManyBodyBasis, index::Int) - result = SparseOperator(b) - # <{m}_i| at |{m}_j> - for i=1:length(b) - occ_i = b.occupations[i] - if occ_i[index] == 0 - continue - end - for j=1:length(b) - if isnonzero(occ_i, b.occupations[j], index) - result.data[i, j] = sqrt(occ_i[index]) - end - end - end - result -end - -""" - destroy(b::ManyBodyBasis, index) - -Annihilation operator for the i-th mode of the many-body basis `b`. -""" -function destroy(b::ManyBodyBasis, index::Int) - result = SparseOperator(b) - # <{m}_j| a |{m}_i> - for i=1:length(b) - occ_i = b.occupations[i] - if occ_i[index] == 0 - continue - end - for j=1:length(b) - if isnonzero(occ_i, b.occupations[j], index) - result.data[j, i] = sqrt(occ_i[index]) - end - end - end - result -end - -""" - number(b::ManyBodyBasis, index) - -Particle number operator for the i-th mode of the many-body basis `b`. -""" -function number(b::ManyBodyBasis, index::Int) - result = SparseOperator(b) - for i=1:length(b) - result.data[i, i] = b.occupations[i][index] - end - result -end - -""" - number(b::ManyBodyBasis) - -Total particle number operator. -""" -function number(b::ManyBodyBasis) - result = SparseOperator(b) - for i=1:length(b) - result.data[i, i] = sum(b.occupations[i]) - end - result -end - -function isnonzero(occ1, occ2, index1::Int, index2::Int) - for i=1:length(occ1) - if i == index1 && i == index2 - if occ1[i] != occ2[i] - return false - end - elseif i == index1 - if occ1[i] != occ2[i] + 1 - return false - end - elseif i == index2 - if occ1[i] != occ2[i] - 1 - return false - end - else - if occ1[i] != occ2[i] - return false - end - end - end - true -end - -""" - transition(b::ManyBodyBasis, to::Int, from::Int) - -Operator ``|\\mathrm{to}⟩⟨\\mathrm{from}|`` transferring particles between modes. -""" -function transition(b::ManyBodyBasis, to::Int, from::Int) - result = SparseOperator(b) - # <{m}_j| at_to a_from |{m}_i> - for i=1:length(b) - occ_i = b.occupations[i] - if occ_i[from] == 0 - continue - end - for j=1:length(b) - occ_j = b.occupations[j] - if isnonzero(occ_j, occ_i, to, from) - result.data[j, i] = sqrt(occ_i[from])*sqrt(occ_j[to]) - end - end - end - result -end - -# Calculate many-Body operator from one-body operator -""" - manybodyoperator(b::ManyBodyBasis, op) - -Create the many-body operator from the given one-body operator `op`. - -The given operator can either be a one-body operator or a -two-body interaction. Higher order interactions are at the -moment not implemented. - -The mathematical formalism for the one-body case is described by - -```math -X = \\sum_{ij} a_i^† a_j ⟨u_i| x | u_j⟩ -``` - -and for the interaction case by - -```math -X = \\sum_{ijkl} a_i^† a_j^† a_k a_l ⟨u_i|⟨u_j| x |u_k⟩|u_l⟩ -``` - -where ``X`` is the N-particle operator, ``x`` is the one-body operator and -``|u⟩`` are the one-body states associated to the -different modes of the N-particle basis. -""" -function manybodyoperator{T<:Operator}(basis::ManyBodyBasis, op::T)::T - @assert op.basis_l == op.basis_r - if op.basis_l == basis.onebodybasis - result = manybodyoperator_1(basis, op) - elseif op.basis_l == basis.onebodybasis ⊗ basis.onebodybasis - result = manybodyoperator_2(basis, op) - else - throw(ArgumentError("The basis of the given operator has to either be equal to b or b ⊗ b where b is the 1st quantization basis associated to the nparticle basis.")) - end - result -end - -function manybodyoperator_1(basis::ManyBodyBasis, op::DenseOperator) - N = length(basis) - S = length(basis.onebodybasis) - result = DenseOperator(basis) - @inbounds for n=1:N, m=1:N - for j=1:S, i=1:S - C = coefficient(basis.occupations[m], basis.occupations[n], [i], [j]) - if C != 0. - result.data[m,n] += C*op.data[i,j] - end - end - end - return result -end - -function manybodyoperator_1(basis::ManyBodyBasis, op::SparseOperator) - N = length(basis) - S = length(basis.onebodybasis) - result = SparseOperator(basis) - M = op.data - @inbounds for colindex = 1:M.n - for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - row = M.rowval[i] - value = M.nzval[i] - for m=1:N, n=1:N - C = coefficient(basis.occupations[m], basis.occupations[n], [row], [colindex]) - if C != 0. - result.data[m, n] += C*value - end - end - end - end - return result -end - -function manybodyoperator_2(basis::ManyBodyBasis, op::DenseOperator) - N = length(basis) - S = length(basis.onebodybasis) - @assert S^2 == length(op.basis_l) - @assert S^2 == length(op.basis_r) - result = DenseOperator(basis) - op_data = reshape(op.data, S, S, S, S) - occupations = basis.occupations - @inbounds for m=1:N, n=1:N - for l=1:S, k=1:S, j=1:S, i=1:S - C = coefficient(occupations[m], occupations[n], [i, j], [k, l]) - result.data[m,n] += C*op_data[i, j, k, l] - end - end - return result -end - -function manybodyoperator_2(basis::ManyBodyBasis, op::SparseOperator) - N = length(basis) - S = length(basis.onebodybasis) - result = SparseOperator(basis) - occupations = basis.occupations - rows = rowvals(op.data) - values = nonzeros(op.data) - @inbounds for column=1:S^2, j in nzrange(op.data, column) - row = rows[j] - value = values[j] - for m=1:N, n=1:N - # println("row:", row, " column:"column, ind_left) - index = ind2sub((S, S, S, S), (column-1)*S^2 + row) - C = coefficient(occupations[m], occupations[n], index[1:2], index[3:4]) - if C!=0. - result.data[m,n] += C*value - end - end - end - return result -end - - -# Calculate expectation value of one-body operator -""" - onebodyexpect(op, state) - -Expectation value of the one-body operator `op` in respect to the many-body `state`. -""" -function onebodyexpect(op::Operator, state::Ket) - @assert isa(state.basis, ManyBodyBasis) - @assert op.basis_l == op.basis_r - if state.basis.onebodybasis == op.basis_l - result = onebodyexpect_1(op, state) - # Not yet implemented: - # elseif state.basis.basis ⊗ state.basis.basis == op.basis_l - # result = onebodyexpect_2(op, state) - else - throw(ArgumentError("The basis of the given operator has to either be equal to b or b ⊗ b where b is the 1st quantization basis associated to the nparticle basis of the state.")) - end - result -end - -function onebodyexpect(op::Operator, state::Operator) - @assert op.basis_l == op.basis_r - @assert state.basis_l == state.basis_r - @assert isa(state.basis_l, ManyBodyBasis) - if state.basis_l.onebodybasis == op.basis_l - result = onebodyexpect_1(op, state) - # Not yet implemented - # elseif state.basis.basis ⊗ state.basis.basis == op.basis_l - # result = onebodyexpect_2(op, state) - else - throw(ArgumentError("The basis of the given operator has to either be equal to b or b ⊗ b where b is the 1st quantization basis associated to the nparticle basis of the state.")) - end - result -end -onebodyexpect(op::Operator, states::Vector) = [onebodyexpect(op, state) for state=states] - -function onebodyexpect_1(op::DenseOperator, state::Ket) - N = length(state.basis) - S = length(state.basis.onebodybasis) - result = complex(0.) - occupations = state.basis.occupations - for m=1:N, n=1:N - value = conj(state.data[m])*state.data[n] - for i=1:S, j=1:S - C = coefficient(occupations[m], occupations[n], [i], [j]) - if C != 0. - result += C*op.data[i,j]*value - end - end - end - result -end - -function onebodyexpect_1(op::DenseOperator, state::DenseOperator) - N = length(state.basis_l) - S = length(state.basis_l.onebodybasis) - result = complex(0.) - occupations = state.basis_l.occupations - @inbounds for s=1:N, t=1:N - value = state.data[t,s] - for i=1:S, j=1:S - C = coefficient(occupations[s], occupations[t], [i], [j]) - if C != 0. - result += C*op.data[i,j]*value - end - end - end - result -end - -function onebodyexpect_1(op::SparseOperator, state::Ket) - N = length(state.basis) - S = length(state.basis.onebodybasis) - result = complex(0.) - occupations = state.basis.occupations - M = op.data - @inbounds for colindex = 1:M.n - for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - row = M.rowval[i] - value = M.nzval[i] - for m=1:N, n=1:N - C = coefficient(occupations[m], occupations[n], [row], [colindex]) - if C != 0. - result += C*value*conj(state.data[m])*state.data[n] - end - end - end - end - result -end - -function onebodyexpect_1(op::SparseOperator, state::DenseOperator) - N = length(state.basis_l) - S = length(state.basis_l.onebodybasis) - result = complex(0.) - occupations = state.basis_l.occupations - M = op.data - @inbounds for colindex = 1:M.n - for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - row = M.rowval[i] - value = M.nzval[i] - for s=1:N, t=1:N - C = coefficient(occupations[s], occupations[t], [row], [colindex]) - if C != 0. - result += C*value*state.data[t,s] - end - end - end - end - result -end - - -""" -Calculate the matrix element <{m}|at_1...at_n a_1...a_n|{n}>. -""" -function coefficient(occ_m, occ_n, at_indices, a_indices) - occ_m = copy(occ_m) - occ_n = copy(occ_n) - C = 1. - for i=at_indices - if occ_m[i] == 0 - return 0. - end - C *= sqrt(occ_m[i]) - occ_m[i] -= 1 - end - for i=a_indices - if occ_n[i] == 0 - return 0. - end - C *= sqrt(occ_n[i]) - occ_n[i] -= 1 - end - if occ_m == occ_n - return C - else - return 0. - end -end - -function _distribute_bosons(Nparticles::Int, Nmodes::Int, index::Int, occupations::Vector{Int}, results::Vector{Vector{Int}}) - if index==Nmodes - occupations[index] = Nparticles - push!(results, copy(occupations)) - else - for n=Nparticles:-1:0 - occupations[index] = n - _distribute_bosons(Nparticles-n, Nmodes, index+1, occupations, results) - end - end - return results -end - -function _distribute_fermions(Nparticles::Int, Nmodes::Int, index::Int, occupations::Vector{Int}, results::Vector{Vector{Int}}) - if (Nmodes-index)+1) @@ -17,14 +7,14 @@ Integrate the master equation with dmaster_h as derivative function. Further information can be found at [`master`](@ref). """ -function master_h(tspan, rho0::DenseOperator, H::Operator, J::Vector; +function master_h(tspan, rho0::T, H::AbstractOperator{B,B}, J::Vector; rates::DecayRates=nothing, Jdagger::Vector=dagger.(J), - fout::Union{Function,Void}=nothing, - kwargs...) + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} check_master(rho0, H, J, Jdagger, rates) tmp = copy(rho0) - dmaster_(t, rho::DenseOperator, drho::DenseOperator) = dmaster_h(rho, H, rates, J, Jdagger, drho, tmp) + dmaster_(t, rho::T, drho::T) = dmaster_h(rho, H, rates, J, Jdagger, drho, tmp) integrate_master(tspan, dmaster_, rho0, fout; kwargs...) end @@ -39,15 +29,15 @@ H_{nh} = H - \\frac{i}{2} \\sum_k J^†_k J_k ``` Further information can be found at [`master`](@ref). """ -function master_nh(tspan, rho0::DenseOperator, Hnh::Operator, J::Vector; +function master_nh(tspan, rho0::T, Hnh::AbstractOperator{B,B}, J::Vector; rates::DecayRates=nothing, - Hnhdagger::Operator=dagger(Hnh), + Hnhdagger::AbstractOperator=dagger(Hnh), Jdagger::Vector=dagger.(J), - fout::Union{Function,Void}=nothing, - kwargs...) + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} check_master(rho0, Hnh, J, Jdagger, rates) tmp = copy(rho0) - dmaster_(t, rho::DenseOperator, drho::DenseOperator) = dmaster_nh(rho, Hnh, Hnhdagger, rates, J, Jdagger, drho, tmp) + dmaster_(t, rho::T, drho::T) = dmaster_nh(rho, Hnh, Hnhdagger, rates, J, Jdagger, drho, tmp) integrate_master(tspan, dmaster_, rho0, fout; kwargs...) end @@ -83,15 +73,15 @@ non-hermitian Hamiltonian and then calls master_nh which is slightly faster. be changed. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function master(tspan, rho0::DenseOperator, H::Operator, J::Vector; +function master(tspan, rho0::T, H::AbstractOperator{B,B}, J::Vector; rates::DecayRates=nothing, Jdagger::Vector=dagger.(J), - fout::Union{Function,Void}=nothing, - kwargs...) + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} isreducible = check_master(rho0, H, J, Jdagger, rates) if !isreducible tmp = copy(rho0) - dmaster_h_(t, rho::DenseOperator, drho::DenseOperator) = dmaster_h(rho, H, rates, J, Jdagger, drho, tmp) + dmaster_h_(t, rho::T, drho::T) = dmaster_h(rho, H, rates, J, Jdagger, drho, tmp) return integrate_master(tspan, dmaster_h_, rho0, fout; kwargs...) else Hnh = copy(H) @@ -110,7 +100,7 @@ function master(tspan, rho0::DenseOperator, H::Operator, J::Vector; end Hnhdagger = dagger(Hnh) tmp = copy(rho0) - dmaster_nh_(t, rho::DenseOperator, drho::DenseOperator) = dmaster_nh(rho, Hnh, Hnhdagger, rates, J, Jdagger, drho, tmp) + dmaster_nh_(t, rho::T, drho::T) = dmaster_nh(rho, Hnh, Hnhdagger, rates, J, Jdagger, drho, tmp) return integrate_master(tspan, dmaster_nh_, rho0, fout; kwargs...) end end @@ -128,12 +118,12 @@ The given function can either be of the form `f(t, rho) -> (Hnh, Hnhdagger, J, J or `f(t, rho) -> (Hnh, Hnhdagger, J, Jdagger, rates)` For further information look at [`master_dynamic`](@ref). """ -function master_nh_dynamic(tspan, rho0::DenseOperator, f::Function; +function master_nh_dynamic(tspan, rho0::T, f::Function; rates::DecayRates=nothing, - fout::Union{Function,Void}=nothing, - kwargs...) + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} tmp = copy(rho0) - dmaster_(t, rho::DenseOperator, drho::DenseOperator) = dmaster_nh_dynamic(t, rho, f, rates, drho, tmp) + dmaster_(t, rho::T, drho::T) = dmaster_nh_dynamic(t, rho, f, rates, drho, tmp) integrate_master(tspan, dmaster_, rho0, fout; kwargs...) end @@ -162,36 +152,36 @@ operators: be changed. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function master_dynamic(tspan, rho0::DenseOperator, f::Function; +function master_dynamic(tspan, rho0::T, f::Function; rates::DecayRates=nothing, - fout::Union{Function,Void}=nothing, - kwargs...) + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} tmp = copy(rho0) - dmaster_(t, rho::DenseOperator, drho::DenseOperator) = dmaster_h_dynamic(t, rho, f, rates, drho, tmp) + dmaster_(t, rho::T, drho::T) = dmaster_h_dynamic(t, rho, f, rates, drho, tmp) integrate_master(tspan, dmaster_, rho0, fout; kwargs...) end # Automatically convert Ket states to density operators -master(tspan, psi0::Ket, H::Operator, J::Vector; kwargs...) = master(tspan, dm(psi0), H, J; kwargs...) -master_h(tspan, psi0::Ket, H::Operator, J::Vector; kwargs...) = master_h(tspan, dm(psi0), H, J; kwargs...) -master_nh(tspan, psi0::Ket, Hnh::Operator, J::Vector; kwargs...) = master_nh(tspan, dm(psi0), Hnh, J; kwargs...) -master_dynamic(tspan, psi0::Ket, f::Function; kwargs...) = master_dynamic(tspan, dm(psi0), f; kwargs...) -master_nh_dynamic(tspan, psi0::Ket, f::Function; kwargs...) = master_nh_dynamic(tspan, dm(psi0), f; kwargs...) +master(tspan, psi0::Ket{B}, H::AbstractOperator{B,B}, J::Vector; kwargs...) where B<:Basis = master(tspan, dm(psi0), H, J; kwargs...) +master_h(tspan, psi0::Ket{B}, H::AbstractOperator{B,B}, J::Vector; kwargs...) where B<:Basis = master_h(tspan, dm(psi0), H, J; kwargs...) +master_nh(tspan, psi0::Ket{B}, Hnh::AbstractOperator{B,B}, J::Vector; kwargs...) where B<:Basis = master_nh(tspan, dm(psi0), Hnh, J; kwargs...) +master_dynamic(tspan, psi0::Ket{B}, f::Function; kwargs...) where B<:Basis = master_dynamic(tspan, dm(psi0), f; kwargs...) +master_nh_dynamic(tspan, psi0::Ket{B}, f::Function; kwargs...) where B<:Basis = master_nh_dynamic(tspan, dm(psi0), f; kwargs...) # Recasting needed for the ODE solver is just providing the underlying data -function recast!(x::Array{Complex128, 2}, rho::DenseOperator) +function recast!(x::T, rho::DenseOperator{B,B,T}) where {B<:Basis,T<:Matrix{ComplexF64}} rho.data = x end -recast!(rho::DenseOperator, x::Array{Complex128, 2}) = nothing +recast!(rho::DenseOperator{B,B,T}, x::T) where {B<:Basis,T<:Matrix{ComplexF64}} = nothing -function integrate_master(tspan, df::Function, rho0::DenseOperator, - fout::Union{Void, Function}; kwargs...) +function integrate_master(tspan, df::Function, rho0::T, + fout::Union{Nothing, Function}; kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} tspan_ = convert(Vector{Float64}, tspan) x0 = rho0.data - state = DenseOperator(rho0.basis_l, rho0.basis_r, rho0.data) - dstate = DenseOperator(rho0.basis_l, rho0.basis_r, rho0.data) + state = T(rho0.basis_l, rho0.basis_r, rho0.data) + dstate = T(rho0.basis_l, rho0.basis_r, rho0.data) integrate(tspan_, df, x0, state, dstate, fout; kwargs...) end @@ -205,139 +195,139 @@ end # the type of the given decay rate object which can either be nothing, a vector # or a matrix. -function dmaster_h(rho::DenseOperator, H::Operator, - rates::Void, J::Vector, Jdagger::Vector, - drho::DenseOperator, tmp::DenseOperator) - operators.gemm!(-1im, H, rho, 0, drho) - operators.gemm!(1im, rho, H, 1, drho) +function dmaster_h(rho::T, H::AbstractOperator{B,B}, + rates::Nothing, J::Vector, Jdagger::Vector, + drho::T, tmp::T) where {B<:Basis,T<:DenseOperator{B,B}} + QuantumOpticsBase.gemm!(-1im, H, rho, 0, drho) + QuantumOpticsBase.gemm!(1im, rho, H, 1, drho) for i=1:length(J) - operators.gemm!(1, J[i], rho, 0, tmp) - operators.gemm!(1, tmp, Jdagger[i], 1, drho) + QuantumOpticsBase.gemm!(1, J[i], rho, 0, tmp) + QuantumOpticsBase.gemm!(1, tmp, Jdagger[i], 1, drho) - operators.gemm!(-0.5, Jdagger[i], tmp, 1, drho) + QuantumOpticsBase.gemm!(-0.5, Jdagger[i], tmp, 1, drho) - operators.gemm!(1., rho, Jdagger[i], 0, tmp) - operators.gemm!(-0.5, tmp, J[i], 1, drho) + QuantumOpticsBase.gemm!(1., rho, Jdagger[i], 0, tmp) + QuantumOpticsBase.gemm!(-0.5, tmp, J[i], 1, drho) end return drho end -function dmaster_h(rho::DenseOperator, H::Operator, +function dmaster_h(rho::T, H::AbstractOperator{B,B}, rates::Vector{Float64}, J::Vector, Jdagger::Vector, - drho::DenseOperator, tmp::DenseOperator) - operators.gemm!(-1im, H, rho, 0, drho) - operators.gemm!(1im, rho, H, 1, drho) + drho::T, tmp::T) where {B<:Basis,T<:DenseOperator{B,B}} + QuantumOpticsBase.gemm!(-1im, H, rho, 0, drho) + QuantumOpticsBase.gemm!(1im, rho, H, 1, drho) for i=1:length(J) - operators.gemm!(rates[i], J[i], rho, 0, tmp) - operators.gemm!(1, tmp, Jdagger[i], 1, drho) + QuantumOpticsBase.gemm!(rates[i], J[i], rho, 0, tmp) + QuantumOpticsBase.gemm!(1, tmp, Jdagger[i], 1, drho) - operators.gemm!(-0.5, Jdagger[i], tmp, 1, drho) + QuantumOpticsBase.gemm!(-0.5, Jdagger[i], tmp, 1, drho) - operators.gemm!(rates[i], rho, Jdagger[i], 0, tmp) - operators.gemm!(-0.5, tmp, J[i], 1, drho) + QuantumOpticsBase.gemm!(rates[i], rho, Jdagger[i], 0, tmp) + QuantumOpticsBase.gemm!(-0.5, tmp, J[i], 1, drho) end return drho end -function dmaster_h(rho::DenseOperator, H::Operator, +function dmaster_h(rho::T, H::AbstractOperator{B,B}, rates::Matrix{Float64}, J::Vector, Jdagger::Vector, - drho::DenseOperator, tmp::DenseOperator) - operators.gemm!(-1im, H, rho, 0, drho) - operators.gemm!(1im, rho, H, 1, drho) + drho::T, tmp::T) where {B<:Basis,T<:DenseOperator{B,B}} + QuantumOpticsBase.gemm!(-1im, H, rho, 0, drho) + QuantumOpticsBase.gemm!(1im, rho, H, 1, drho) for j=1:length(J), i=1:length(J) - operators.gemm!(rates[i,j], J[i], rho, 0, tmp) - operators.gemm!(1, tmp, Jdagger[j], 1, drho) + QuantumOpticsBase.gemm!(rates[i,j], J[i], rho, 0, tmp) + QuantumOpticsBase.gemm!(1, tmp, Jdagger[j], 1, drho) - operators.gemm!(-0.5, Jdagger[j], tmp, 1, drho) + QuantumOpticsBase.gemm!(-0.5, Jdagger[j], tmp, 1, drho) - operators.gemm!(rates[i,j], rho, Jdagger[j], 0, tmp) - operators.gemm!(-0.5, tmp, J[i], 1, drho) + QuantumOpticsBase.gemm!(rates[i,j], rho, Jdagger[j], 0, tmp) + QuantumOpticsBase.gemm!(-0.5, tmp, J[i], 1, drho) end return drho end -function dmaster_nh(rho::DenseOperator, Hnh::Operator, Hnh_dagger::Operator, - rates::Void, J::Vector, Jdagger::Vector, - drho::DenseOperator, tmp::DenseOperator) - operators.gemm!(-1im, Hnh, rho, 0, drho) - operators.gemm!(1im, rho, Hnh_dagger, 1, drho) +function dmaster_nh(rho::T1, Hnh::T2, Hnh_dagger::T2, + rates::Nothing, J::Vector, Jdagger::Vector, + drho::T1, tmp::T1) where {B<:Basis,T1<:DenseOperator{B,B},T2<:AbstractOperator{B,B}} + QuantumOpticsBase.gemm!(-1im, Hnh, rho, 0, drho) + QuantumOpticsBase.gemm!(1im, rho, Hnh_dagger, 1, drho) for i=1:length(J) - operators.gemm!(1, J[i], rho, 0, tmp) - operators.gemm!(1, tmp, Jdagger[i], 1, drho) + QuantumOpticsBase.gemm!(1, J[i], rho, 0, tmp) + QuantumOpticsBase.gemm!(1, tmp, Jdagger[i], 1, drho) end return drho end -function dmaster_nh(rho::DenseOperator, Hnh::Operator, Hnh_dagger::Operator, +function dmaster_nh(rho::T1, Hnh::T2, Hnh_dagger::T2, rates::Vector{Float64}, J::Vector, Jdagger::Vector, - drho::DenseOperator, tmp::DenseOperator) - operators.gemm!(-1im, Hnh, rho, 0, drho) - operators.gemm!(1im, rho, Hnh_dagger, 1, drho) + drho::T1, tmp::T1) where {B<:Basis,T1<:DenseOperator{B,B},T2<:AbstractOperator{B,B}} + QuantumOpticsBase.gemm!(-1im, Hnh, rho, 0, drho) + QuantumOpticsBase.gemm!(1im, rho, Hnh_dagger, 1, drho) for i=1:length(J) - operators.gemm!(rates[i], J[i], rho, 0, tmp) - operators.gemm!(1, tmp, Jdagger[i], 1, drho) + QuantumOpticsBase.gemm!(rates[i], J[i], rho, 0, tmp) + QuantumOpticsBase.gemm!(1, tmp, Jdagger[i], 1, drho) end return drho end -function dmaster_nh(rho::DenseOperator, Hnh::Operator, Hnh_dagger::Operator, +function dmaster_nh(rho::T1, Hnh::T2, Hnh_dagger::T2, rates::Matrix{Float64}, J::Vector, Jdagger::Vector, - drho::DenseOperator, tmp::DenseOperator) - operators.gemm!(-1im, Hnh, rho, 0, drho) - operators.gemm!(1im, rho, Hnh_dagger, 1, drho) + drho::T1, tmp::T1) where {B<:Basis,T1<:DenseOperator{B,B},T2<:AbstractOperator{B,B}} + QuantumOpticsBase.gemm!(-1im, Hnh, rho, 0, drho) + QuantumOpticsBase.gemm!(1im, rho, Hnh_dagger, 1, drho) for j=1:length(J), i=1:length(J) - operators.gemm!(rates[i,j], J[i], rho, 0, tmp) - operators.gemm!(1, tmp, Jdagger[j], 1, drho) + QuantumOpticsBase.gemm!(rates[i,j], J[i], rho, 0, tmp) + QuantumOpticsBase.gemm!(1, tmp, Jdagger[j], 1, drho) end return drho end -function dmaster_h_dynamic(t::Float64, rho::DenseOperator, f::Function, +function dmaster_h_dynamic(t::Float64, rho::T, f::Function, rates::DecayRates, - drho::DenseOperator, tmp::DenseOperator) + drho::T, tmp::T) where {B<:Basis,T<:DenseOperator{B,B}} result = f(t, rho) - @assert 3 <= length(result) <= 4 + QO_CHECKS[] && @assert 3 <= length(result) <= 4 if length(result) == 3 H, J, Jdagger = result rates_ = rates else H, J, Jdagger, rates_ = result end - check_master(rho, H, J, Jdagger, rates_) + QO_CHECKS[] && check_master(rho, H, J, Jdagger, rates_) dmaster_h(rho, H, rates_, J, Jdagger, drho, tmp) end -function dmaster_nh_dynamic(t::Float64, rho::DenseOperator, f::Function, +function dmaster_nh_dynamic(t::Float64, rho::T, f::Function, rates::DecayRates, - drho::DenseOperator, tmp::DenseOperator) + drho::T, tmp::T) where {B<:Basis,T<:DenseOperator{B,B}} result = f(t, rho) - @assert 4 <= length(result) <= 5 + QO_CHECKS[] && @assert 4 <= length(result) <= 5 if length(result) == 4 Hnh, Hnh_dagger, J, Jdagger = result rates_ = rates else Hnh, Hnh_dagger, J, Jdagger, rates_ = result end - check_master(rho, Hnh, J, Jdagger, rates_) + QO_CHECKS[] && check_master(rho, Hnh, J, Jdagger, rates_) dmaster_nh(rho, Hnh, Hnh_dagger, rates_, J, Jdagger, drho, tmp) end -function check_master(rho0::DenseOperator, H::Operator, J::Vector, Jdagger::Vector, rates::DecayRates) +function check_master(rho0::DenseOperator{B,B}, H::AbstractOperator{B,B}, J::Vector, Jdagger::Vector, rates::DecayRates) where B<:Basis + # TODO: clean up type checks by dispatch; make type of J known isreducible = true # test if all operators are sparse or dense - check_samebases(rho0, H) if !(isa(H, DenseOperator) || isa(H, SparseOperator)) isreducible = false end for j=J - @assert isa(j, Operator) + @assert isa(j, AbstractOperator{B,B}) if !(isa(j, DenseOperator) || isa(j, SparseOperator)) isreducible = false end check_samebases(rho0, j) end for j=Jdagger - @assert isa(j, Operator) + @assert isa(j, AbstractOperator{B,B}) if !(isa(j, DenseOperator) || isa(j, SparseOperator)) isreducible = false end @@ -351,5 +341,3 @@ function check_master(rho0::DenseOperator, H::Operator, J::Vector, Jdagger::Vect end isreducible end - -end #module diff --git a/src/mcwf.jl b/src/mcwf.jl index 6ffd4c4f..459deec2 100644 --- a/src/mcwf.jl +++ b/src/mcwf.jl @@ -1,18 +1,7 @@ -module timeevolution_mcwf +using Random, LinearAlgebra -export mcwf, mcwf_h, mcwf_nh, mcwf_dynamic, mcwf_nh_dynamic, diagonaljumps - -using ...bases, ...states, ...operators -using ...operators_dense, ...operators_sparse -using ..timeevolution -using ...operators_lazysum, ...operators_lazytensor, ...operators_lazyproduct -import OrdinaryDiffEq # TODO: Remove imports -import DiffEqCallbacks, RecursiveArrayTools.copyat_or_push! -import ..recast! -Base.@pure pure_inference(fout,T) = Core.Inference.return_type(fout, T) - -const DecayRates = Union{Vector{Float64}, Matrix{Float64}, Void} +import RecursiveArrayTools.copyat_or_push! """ mcwf_h(tspan, rho0, Hnh, J; ) @@ -21,19 +10,19 @@ Calculate MCWF trajectory where the Hamiltonian is given in hermitian form. For more information see: [`mcwf`](@ref) """ -function mcwf_h(tspan, psi0::Ket, H::Operator, J::Vector; - seed=rand(UInt), rates::DecayRates=nothing, - fout=nothing, Jdagger::Vector=dagger.(J), - tmp::Ket=copy(psi0), - display_beforeevent=false, display_afterevent=false, - kwargs...) +function mcwf_h(tspan, psi0::T, H::AbstractOperator{B,B}, J::Vector; + seed=rand(UInt), rates::DecayRates=nothing, + fout=nothing, Jdagger::Vector=dagger.(J), + tmp::T=copy(psi0), + display_beforeevent=false, display_afterevent=false, + kwargs...) where {B<:Basis,T<:Ket{B}} check_mcwf(psi0, H, J, Jdagger, rates) - f(t, psi, dpsi) = dmcwf_h(psi, H, J, Jdagger, dpsi, tmp, rates) - j(rng, t, psi, psi_new) = jump(rng, t, psi, J, psi_new, rates) - return integrate_mcwf(f, j, tspan, psi0, seed, fout; - display_beforeevent=display_beforeevent, - display_afterevent=display_afterevent, - kwargs...) + f(t::Float64, psi::T, dpsi::T) = dmcwf_h(psi, H, J, Jdagger, dpsi, tmp, rates) + j(rng, t::Float64, psi::T, psi_new::T) = jump(rng, t, psi, J, psi_new, rates) + integrate_mcwf(f, j, tspan, psi0, seed, fout; + display_beforeevent=display_beforeevent, + display_afterevent=display_afterevent, + kwargs...) end """ @@ -47,17 +36,17 @@ H_{nh} = H - \\frac{i}{2} \\sum_k J^†_k J_k For more information see: [`mcwf`](@ref) """ -function mcwf_nh(tspan, psi0::Ket, Hnh::Operator, J::Vector; - seed=rand(UInt), fout=nothing, - display_beforeevent=false, display_afterevent=false, - kwargs...) +function mcwf_nh(tspan, psi0::T, Hnh::AbstractOperator{B,B}, J::Vector; + seed=rand(UInt), fout=nothing, + display_beforeevent=false, display_afterevent=false, + kwargs...) where {B<:Basis,T<:Ket{B}} check_mcwf(psi0, Hnh, J, J, nothing) - f(t, psi, dpsi) = dmcwf_nh(psi, Hnh, dpsi) - j(rng, t, psi, psi_new) = jump(rng, t, psi, J, psi_new, nothing) - return integrate_mcwf(f, j, tspan, psi0, seed, fout; - display_beforeevent=display_beforeevent, - display_afterevent=display_afterevent, - kwargs...) + f(t::Float64, psi::T, dpsi::T) = dmcwf_nh(psi, Hnh, dpsi) + j(rng, t::Float64, psi::T, psi_new::T) = jump(rng, t, psi, J, psi_new, nothing) + integrate_mcwf(f, j, tspan, psi0, seed, fout; + display_beforeevent=display_beforeevent, + display_afterevent=display_afterevent, + kwargs...) end """ @@ -93,26 +82,29 @@ and therefore must not be changed. operators. If they are not given they are calculated automatically. * `display_beforeevent=false`: `fout` is called before every jump. * `display_afterevent=false`: `fout` is called after every jump. +* `display_jumps=false`: If set to true, an additional list of times and indices +is returned. These correspond to the times at which a jump occured and the index +of the jump operators with which the jump occured, respectively. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function mcwf(tspan, psi0::Ket, H::Operator, J::Vector; - seed=rand(UInt), rates::DecayRates=nothing, - fout=nothing, Jdagger::Vector=dagger.(J), - display_beforeevent=false, display_afterevent=false, - kwargs...) +function mcwf(tspan, psi0::T, H::AbstractOperator{B,B}, J::Vector; + seed=rand(UInt), rates::DecayRates=nothing, + fout=nothing, Jdagger::Vector=dagger.(J), + display_beforeevent=false, display_afterevent=false, + kwargs...) where {B<:Basis,T<:Ket{B}} isreducible = check_mcwf(psi0, H, J, Jdagger, rates) if !isreducible tmp = copy(psi0) - dmcwf_h_(t, psi, dpsi) = dmcwf_h(psi, H, J, Jdagger, dpsi, tmp, rates) - j_h(rng, t, psi, psi_new) = jump(rng, t, psi, J, psi_new, rates) - return integrate_mcwf(dmcwf_h_, j_h, tspan, psi0, seed, - fout; - display_beforeevent=display_beforeevent, - display_afterevent=display_afterevent, - kwargs...) + dmcwf_h_(t::Float64, psi::T, dpsi::T) = dmcwf_h(psi, H, J, Jdagger, dpsi, tmp, rates) + j_h(rng, t::Float64, psi::T, psi_new::T) = jump(rng, t, psi, J, psi_new, rates) + integrate_mcwf(dmcwf_h_, j_h, tspan, psi0, seed, + fout; + display_beforeevent=display_beforeevent, + display_afterevent=display_afterevent, + kwargs...) else Hnh = copy(H) - if typeof(rates) == Void + if typeof(rates) == Nothing for i=1:length(J) Hnh -= 0.5im*Jdagger[i]*J[i] end @@ -121,13 +113,13 @@ function mcwf(tspan, psi0::Ket, H::Operator, J::Vector; Hnh -= 0.5im*rates[i]*Jdagger[i]*J[i] end end - dmcwf_nh_(t, psi, dpsi) = dmcwf_nh(psi, Hnh, dpsi) - j_nh(rng, t, psi, psi_new) = jump(rng, t, psi, J, psi_new, rates) - return integrate_mcwf(dmcwf_nh_, j_nh, tspan, psi0, seed, - fout; - display_beforeevent=display_beforeevent, - display_afterevent=display_afterevent, - kwargs...) + dmcwf_nh_(t::Float64, psi::T, dpsi::T) = dmcwf_nh(psi, Hnh, dpsi) + j_nh(rng, t::Float64, psi::T, psi_new::T) = jump(rng, t, psi, J, psi_new, rates) + integrate_mcwf(dmcwf_nh_, j_nh, tspan, psi0, seed, + fout; + display_beforeevent=display_beforeevent, + display_afterevent=display_afterevent, + kwargs...) end end @@ -155,15 +147,18 @@ normalized nor permanent! It is still in use by the ode solve and therefore must not be changed. * `display_beforeevent=false`: `fout` is called before every jump. * `display_afterevent=false`: `fout` is called after every jump. +* `display_jumps=false`: If set to true, an additional list of times and indices +is returned. These correspond to the times at which a jump occured and the index +of the jump operators with which the jump occured, respectively. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function mcwf_dynamic(tspan, psi0::Ket, f::Function; +function mcwf_dynamic(tspan, psi0::T, f::Function; seed=rand(UInt), rates::DecayRates=nothing, fout=nothing, display_beforeevent=false, display_afterevent=false, - kwargs...) + kwargs...) where {T<:Ket} tmp = copy(psi0) - dmcwf_(t, psi, dpsi) = dmcwf_h_dynamic(t, psi, f, rates, dpsi, tmp) - j_(rng, t, psi, psi_new) = jump_dynamic(rng, t, psi, f, psi_new, rates) + dmcwf_(t::Float64, psi::T, dpsi::T) = dmcwf_h_dynamic(t, psi, f, rates, dpsi, tmp) + j_(rng, t::Float64, psi::T, psi_new::T) = jump_dynamic(rng, t, psi, f, psi_new, rates) integrate_mcwf(dmcwf_, j_, tspan, psi0, seed, fout; display_beforeevent=display_beforeevent, @@ -178,12 +173,12 @@ Calculate MCWF trajectory where the dynamic Hamiltonian is given in non-hermitia For more information see: [`mcwf_dynamic`](@ref) """ -function mcwf_nh_dynamic(tspan, psi0::Ket, f::Function; +function mcwf_nh_dynamic(tspan, psi0::T, f::Function; seed=rand(UInt), rates::DecayRates=nothing, fout=nothing, display_beforeevent=false, display_afterevent=false, - kwargs...) - dmcwf_(t, psi, dpsi) = dmcwf_nh_dynamic(t, psi, f, dpsi) - j_(rng, t, psi, psi_new) = jump_dynamic(rng, t, psi, f, psi_new, rates) + kwargs...) where T<:Ket + dmcwf_(t::Float64, psi::T, dpsi::T) = dmcwf_nh_dynamic(t, psi, f, dpsi) + j_(rng, t::Float64, psi::T, psi_new::T) = jump_dynamic(rng, t, psi, f, psi_new, rates) integrate_mcwf(dmcwf_, j_, tspan, psi0, seed, fout; display_beforeevent=display_beforeevent, @@ -191,38 +186,38 @@ function mcwf_nh_dynamic(tspan, psi0::Ket, f::Function; kwargs...) end -function dmcwf_h_dynamic(t::Float64, psi::Ket, f::Function, rates::DecayRates, - dpsi::Ket, tmp::Ket) +function dmcwf_h_dynamic(t::Float64, psi::T, f::Function, rates::DecayRates, + dpsi::T, tmp::T) where T<:Ket result = f(t, psi) - @assert 3 <= length(result) <= 4 + QO_CHECKS[] && @assert 3 <= length(result) <= 4 if length(result) == 3 H, J, Jdagger = result rates_ = rates else H, J, Jdagger, rates_ = result end - check_mcwf(psi, H, J, Jdagger, rates_) - dmcwf_h(psi, H, J, Jdagger, dpsi, tmp, rates) + QO_CHECKS[] && check_mcwf(psi, H, J, Jdagger, rates_) + dmcwf_h(psi, H, J, Jdagger, dpsi, tmp, rates_) end -function dmcwf_nh_dynamic(t::Float64, psi::Ket, f::Function, dpsi::Ket) +function dmcwf_nh_dynamic(t::Float64, psi::T, f::Function, dpsi::T) where T<:Ket result = f(t, psi) - @assert 3 <= length(result) <= 4 + QO_CHECKS[] && @assert 3 <= length(result) <= 4 H, J, Jdagger = result[1:3] - check_mcwf(psi, H, J, Jdagger, nothing) + QO_CHECKS[] && check_mcwf(psi, H, J, Jdagger, nothing) dmcwf_nh(psi, H, dpsi) end -function jump_dynamic(rng, t::Float64, psi::Ket, f::Function, psi_new::Ket, rates::DecayRates) +function jump_dynamic(rng, t::Float64, psi::T, f::Function, psi_new::T, rates::DecayRates) where T<:Ket result = f(t, psi) - @assert 3 <= length(result) <= 4 + QO_CHECKS[] && @assert 3 <= length(result) <= 4 J = result[2] if length(result) == 3 rates_ = rates else rates_ = result[4] end - jump(rng, t::Float64, psi::Ket, J::Vector, psi_new::Ket, rates::DecayRates) + jump(rng, t, psi, J, psi_new, rates_) end """ @@ -247,113 +242,118 @@ Integrate a single Monte Carlo wave function trajectory. * `kwargs`: Further arguments are passed on to the ode solver. """ function integrate_mcwf(dmcwf::Function, jumpfun::Function, tspan, - psi0::Ket, seed, fout::Function; + psi0::T, seed, fout::Function; display_beforeevent=false, display_afterevent=false, - #TODO: Remove kwargs + display_jumps=false, save_everystep=false, callback=nothing, alg=OrdinaryDiffEq.DP5(), - kwargs...) - - tmp = copy(psi0) - as_ket(x::Vector{Complex128}) = Ket(psi0.basis, x) - as_vector(psi::Ket) = psi.data - rng = MersenneTwister(convert(UInt, seed)) - jumpnorm = Ref(rand(rng)) - djumpnorm(x::Vector{Complex128}, t, integrator) = norm(as_ket(x))^2 - (1-jumpnorm[]) - - if !display_beforeevent && !display_afterevent - function dojump(integrator) - x = integrator.u - t = integrator.t - jumpfun(rng, t, as_ket(x), tmp) - x .= tmp.data - jumpnorm[] = rand(rng) + kwargs...) where T + + # Display before or after events + function save_func!(affect!,integrator) + affect!.saveiter += 1 + copyat_or_push!(affect!.saved_values.t, affect!.saveiter, integrator.t) + copyat_or_push!(affect!.saved_values.saveval, affect!.saveiter, + affect!.save_func(integrator.u, integrator.t, integrator),Val{false}) + return nothing + end + save_before! = display_beforeevent ? save_func! : (affect!,integrator)->nothing + save_after! = display_afterevent ? save_func! : (affect!,integrator)->nothing + + # Display jump operator index and times + jump_t = Float64[] + jump_index = Int[] + save_t_index = if display_jumps + function(t,i) + push!(jump_t,t) + push!(jump_index,i) + return nothing end - cb = OrdinaryDiffEq.ContinuousCallback(djumpnorm,dojump, - save_positions = (display_beforeevent,display_afterevent)) - - - return timeevolution.integrate(float(tspan), dmcwf, as_vector(psi0), - copy(psi0), copy(psi0), fout; - callback = cb, - kwargs...) else - # Temporary workaround until proper tooling for saving - # TODO: Replace by proper call to timeevolution.integrate - function fout_(x::Vector{Complex128}, t::Float64, integrator) - recast!(x, state) - fout(t, state) - end - - state = copy(psi0) - dstate = copy(psi0) - out_type = pure_inference(fout, Tuple{eltype(tspan),typeof(state)}) - out = DiffEqCallbacks.SavedValues(Float64,out_type) - scb = DiffEqCallbacks.SavingCallback(fout_,out,saveat=tspan, - save_everystep=save_everystep, - save_start = false) - - function dojump_display(integrator) - x = integrator.u - t = integrator.t - - affect! = scb.affect! - if display_beforeevent - affect!.saveiter += 1 - copyat_or_push!(affect!.saved_values.t, affect!.saveiter, integrator.t) - copyat_or_push!(affect!.saved_values.saveval, affect!.saveiter, - affect!.save_func(integrator.u, integrator.t, integrator),Val{false}) - end - - jumpfun(rng, t, as_ket(x), tmp) - - if display_afterevent - affect!.saveiter += 1 - copyat_or_push!(affect!.saved_values.t, affect!.saveiter, integrator.t) - copyat_or_push!(affect!.saved_values.saveval, affect!.saveiter, - affect!.save_func(integrator.u, integrator.t, integrator),Val{false}) - end + (t,i)->nothing + end - x .= tmp.data - jumpnorm[] = rand(rng) - end + function fout_(x::Vector{ComplexF64}, t::Float64, integrator) + recast!(x, state) + fout(t, state) + end - cb = OrdinaryDiffEq.ContinuousCallback(djumpnorm,dojump_display, - save_positions = (false,false)) - full_cb = OrdinaryDiffEq.CallbackSet(callback,cb,scb) + state = copy(psi0) + dstate = copy(psi0) + out_type = pure_inference(fout, Tuple{eltype(tspan),typeof(state)}) + out = DiffEqCallbacks.SavedValues(Float64,out_type) + scb = DiffEqCallbacks.SavingCallback(fout_,out,saveat=tspan, + save_everystep=save_everystep, + save_start = false) + + cb = jump_callback(jumpfun, seed, scb, save_before!, save_after!, save_t_index, psi0) + full_cb = OrdinaryDiffEq.CallbackSet(callback,cb,scb) + + function df_(dx::D, x::D, p, t) where D<:Vector{ComplexF64} + recast!(x, state) + recast!(dx, dstate) + dmcwf(t, state, dstate) + recast!(dstate, dx) + end - function df_(dx::Vector{Complex128}, x::Vector{Complex128}, p, t) - recast!(x, state) - recast!(dx, dstate) - dmcwf(t, state, dstate) - recast!(dstate, dx) - end + prob = OrdinaryDiffEq.ODEProblem{true}(df_, as_vector(psi0),(tspan[1],tspan[end])) - prob = OrdinaryDiffEq.ODEProblem{true}(df_, as_vector(psi0),(tspan[1],tspan[end])) + sol = OrdinaryDiffEq.solve( + prob, + alg; + reltol = 1.0e-6, + abstol = 1.0e-8, + save_everystep = false, save_start = false, + save_end = false, + callback=full_cb, kwargs...) - sol = OrdinaryDiffEq.solve( - prob, - alg; - reltol = 1.0e-6, - abstol = 1.0e-8, - save_everystep = false, save_start = false, - save_end = false, - callback=full_cb, kwargs...) + if display_jumps + return out.t, out.saveval, jump_t, jump_index + else return out.t, out.saveval end end function integrate_mcwf(dmcwf::Function, jumpfun::Function, tspan, - psi0::Ket, seed, fout::Void; - kwargs...) - function fout_(t, x) - psi = copy(x) - psi /= norm(psi) - return psi + psi0::T, seed, fout::Nothing; + kwargs...) where T + function fout_(t::Float64, x::T) + return normalize(x) end integrate_mcwf(dmcwf, jumpfun, tspan, psi0, seed, fout_; kwargs...) end +function jump_callback(jumpfun::Function, seed, scb, save_before!::Function, + save_after!::Function, save_t_index::Function, psi0::Ket) + + tmp = copy(psi0) + psi_tmp = copy(psi0) + + rng = MersenneTwister(convert(UInt, seed)) + jumpnorm = Ref(rand(rng)) + djumpnorm(x::Vector{ComplexF64}, t::Float64, integrator) = norm(x)^2 - (1-jumpnorm[]) + + function dojump(integrator) + x = integrator.u + t = integrator.t + + affect! = scb.affect! + save_before!(affect!,integrator) + recast!(x, psi_tmp) + i = jumpfun(rng, t, psi_tmp, tmp) + x .= tmp.data + save_after!(affect!,integrator) + save_t_index(t,i) + + jumpnorm[] = rand(rng) + return nothing + end + + return OrdinaryDiffEq.ContinuousCallback(djumpnorm,dojump, + save_positions = (false,false)) +end +as_vector(psi::StateVector) = psi.data + """ jump(rng, t, psi, J, psi_new) @@ -366,40 +366,42 @@ Default jump function. * `J`: List of jump operators. * `psi_new`: Result of jump. """ -function jump(rng, t::Float64, psi::Ket, J::Vector, psi_new::Ket, rates::Void) +function jump(rng, t::Float64, psi::T, J::Vector, psi_new::T, rates::Nothing) where T<:Ket if length(J)==1 - operators.gemv!(complex(1.), J[1], psi, complex(0.), psi_new) + QuantumOpticsBase.gemv!(complex(1.), J[1], psi, complex(0.), psi_new) psi_new.data ./= norm(psi_new) + i=1 else probs = zeros(Float64, length(J)) for i=1:length(J) - operators.gemv!(complex(1.), J[i], psi, complex(0.), psi_new) + QuantumOpticsBase.gemv!(complex(1.), J[i], psi, complex(0.), psi_new) probs[i] = dot(psi_new.data, psi_new.data) end cumprobs = cumsum(probs./sum(probs)) r = rand(rng) i = findfirst(cumprobs.>r) - operators.gemv!(complex(1.)/sqrt(probs[i]), J[i], psi, complex(0.), psi_new) + QuantumOpticsBase.gemv!(complex(1.)/sqrt(probs[i]), J[i], psi, complex(0.), psi_new) end - return nothing + return i end -function jump(rng, t::Float64, psi::Ket, J::Vector, psi_new::Ket, rates::Vector{Float64}) +function jump(rng, t::Float64, psi::T, J::Vector, psi_new::T, rates::Vector{Float64}) where T<:Ket if length(J)==1 - operators.gemv!(complex(sqrt(rates[1])), J[1], psi, complex(0.), psi_new) + QuantumOpticsBase.gemv!(complex(sqrt(rates[1])), J[1], psi, complex(0.), psi_new) psi_new.data ./= norm(psi_new) + i=1 else probs = zeros(Float64, length(J)) for i=1:length(J) - operators.gemv!(complex(sqrt(rates[i])), J[i], psi, complex(0.), psi_new) + QuantumOpticsBase.gemv!(complex(sqrt(rates[i])), J[i], psi, complex(0.), psi_new) probs[i] = dot(psi_new.data, psi_new.data) end cumprobs = cumsum(probs./sum(probs)) r = rand(rng) i = findfirst(cumprobs.>r) - operators.gemv!(complex(sqrt(rates[i]/probs[i])), J[i], psi, complex(0.), psi_new) + QuantumOpticsBase.gemv!(complex(sqrt(rates[i]/probs[i])), J[i], psi, complex(0.), psi_new) end - return nothing + return i end """ @@ -408,22 +410,22 @@ Evaluate non-hermitian Schroedinger equation. The non-hermitian Hamiltonian is given in two parts - the hermitian part H and the jump operators J. """ -function dmcwf_h(psi::Ket, H::Operator, - J::Vector, Jdagger::Vector, dpsi::Ket, tmp::Ket, rates::Void) - operators.gemv!(complex(0,-1.), H, psi, complex(0.), dpsi) +function dmcwf_h(psi::T, H::AbstractOperator{B,B}, + J::Vector, Jdagger::Vector, dpsi::T, tmp::T, rates::Nothing) where {B<:Basis,T<:Ket{B}} + QuantumOpticsBase.gemv!(complex(0,-1.), H, psi, complex(0.), dpsi) for i=1:length(J) - operators.gemv!(complex(1.), J[i], psi, complex(0.), tmp) - operators.gemv!(-complex(0.5,0.), Jdagger[i], tmp, complex(1.), dpsi) + QuantumOpticsBase.gemv!(complex(1.), J[i], psi, complex(0.), tmp) + QuantumOpticsBase.gemv!(-complex(0.5,0.), Jdagger[i], tmp, complex(1.), dpsi) end return dpsi end -function dmcwf_h(psi::Ket, H::Operator, - J::Vector, Jdagger::Vector, dpsi::Ket, tmp::Ket, rates::Vector{Float64}) - operators.gemv!(complex(0,-1.), H, psi, complex(0.), dpsi) +function dmcwf_h(psi::T, H::AbstractOperator{B,B}, + J::Vector, Jdagger::Vector, dpsi::T, tmp::T, rates::Vector{Float64}) where {B<:Basis,T<:Ket{B}} + QuantumOpticsBase.gemv!(complex(0,-1.), H, psi, complex(0.), dpsi) for i=1:length(J) - operators.gemv!(complex(rates[i]), J[i], psi, complex(0.), tmp) - operators.gemv!(-complex(0.5,0.), Jdagger[i], tmp, complex(1.), dpsi) + QuantumOpticsBase.gemv!(complex(rates[i]), J[i], psi, complex(0.), tmp) + QuantumOpticsBase.gemv!(-complex(0.5,0.), Jdagger[i], tmp, complex(1.), dpsi) end return dpsi end @@ -434,8 +436,8 @@ Evaluate non-hermitian Schroedinger equation. The given Hamiltonian is already the non-hermitian version. """ -function dmcwf_nh(psi::Ket, Hnh::Operator, dpsi::Ket) - operators.gemv!(complex(0,-1.), Hnh, psi, complex(0.), dpsi) +function dmcwf_nh(psi::T, Hnh::AbstractOperator{B,B}, dpsi::T) where {B<:Basis,T<:Ket{B}} + QuantumOpticsBase.gemv!(complex(0,-1.), Hnh, psi, complex(0.), dpsi) return dpsi end @@ -444,25 +446,23 @@ end Check input of mcwf. """ -function check_mcwf(psi0::Ket, H::Operator, J::Vector, Jdagger::Vector, rates::DecayRates) +function check_mcwf(psi0::Ket{B}, H::AbstractOperator{B,B}, J::Vector, Jdagger::Vector, rates::DecayRates) where B<:Basis + # TODO: replace type checks by dispatch; make types of J known isreducible = true - check_samebases(basis(psi0), basis(H)) if !(isa(H, DenseOperator) || isa(H, SparseOperator)) isreducible = false end for j=J - @assert isa(j, Operator) + @assert isa(j, AbstractOperator{B,B}) if !(isa(j, DenseOperator) || isa(j, SparseOperator)) isreducible = false end - check_samebases(H, j) end for j=Jdagger - @assert isa(j, Operator) + @assert isa(j, AbstractOperator{B,B}) if !(isa(j, DenseOperator) || isa(j, SparseOperator)) isreducible = false end - check_samebases(H, j) end @assert length(J) == length(Jdagger) if typeof(rates) == Matrix{Float64} @@ -486,16 +486,14 @@ corresponding set of jump operators is calculated. * `rates`: Matrix of decay rates. * `J`: Vector of jump operators. """ -function diagonaljumps(rates::Matrix{Float64}, J::Vector{T}) where T <: Operator +function diagonaljumps(rates::Matrix{Float64}, J::Vector{T}) where {B<:Basis,T<:AbstractOperator{B,B}} @assert length(J) == size(rates)[1] == size(rates)[2] - d, v = eig(rates) + d, v = eigen(rates) d, [sum([v[j, i]*J[j] for j=1:length(d)]) for i=1:length(d)] end -function diagonaljumps(rates::Matrix{Float64}, J::Vector{T}) where T <: Union{LazySum, LazyTensor, LazyProduct} +function diagonaljumps(rates::Matrix{Float64}, J::Vector{T}) where {B<:Basis,T<:Union{LazySum{B,B},LazyTensor{B,B},LazyProduct{B,B}}} @assert length(J) == size(rates)[1] == size(rates)[2] - d, v = eig(rates) + d, v = eigen(rates) d, [LazySum([v[j, i]*J[j] for j=1:length(d)]...) for i=1:length(d)] end - -end #module diff --git a/src/metrics.jl b/src/metrics.jl deleted file mode 100644 index 216c509a..00000000 --- a/src/metrics.jl +++ /dev/null @@ -1,242 +0,0 @@ -module metrics - -export tracenorm, tracenorm_h, tracenorm_nh, - tracedistance, tracedistance_h, tracedistance_nh, - entropy_vn, fidelity, ptranspose, PPT, negativity, - logarithmic_negativity - -using ..bases, ..operators, ..operators_dense, ..states - -""" - tracenorm(rho) - -Trace norm of `rho`. - -It is defined as - -```math -T(ρ) = Tr\\{\\sqrt{ρ^† ρ}\\}. -``` - -Depending if `rho` is hermitian either [`tracenorm_h`](@ref) or -[`tracenorm_nh`](@ref) is called. -""" -function tracenorm(rho::DenseOperator) - check_samebases(rho) - ishermitian(rho) ? tracenorm_h(rho) : tracenorm_nh(rho) -end -function tracenorm(rho::T) where T<:Operator - throw(ArgumentError("tracenorm not implemented for $(T). Use dense operators instead.")) -end - -""" - tracenorm_h(rho) - -Trace norm of `rho`. - -It uses the identity - -```math -T(ρ) = Tr\\{\\sqrt{ρ^† ρ}\\} = \\sum_i |λ_i| -``` - -where ``λ_i`` are the eigenvalues of `rho`. -""" -function tracenorm_h(rho::DenseOperator) - check_samebases(rho) - s = eigvals(Hermitian(rho.data)) - sum(abs.(s)) -end -function tracenorm_h(rho::T) where T<:Operator - throw(ArgumentError("tracenorm_h not implemented for $(T). Use dense operators instead.")) -end - - -""" - tracenorm_nh(rho) - -Trace norm of `rho`. - -Note that in this case `rho` doesn't have to be represented by a square -matrix (i.e. it can have different left-hand and right-hand bases). - -It uses the identity - -```math - T(ρ) = Tr\\{\\sqrt{ρ^† ρ}\\} = \\sum_i σ_i -``` - -where ``σ_i`` are the singular values of `rho`. -""" -tracenorm_nh(rho::DenseOperator) = sum(svdvals(rho.data)) -function tracenorm_nh(rho::T) where T<:Operator - throw(ArgumentError("tracenorm_nh not implemented for $(T). Use dense operators instead.")) -end - - -""" - tracedistance(rho, sigma) - -Trace distance between `rho` and `sigma`. - -It is defined as - -```math -T(ρ,σ) = \\frac{1}{2} Tr\\{\\sqrt{(ρ - σ)^† (ρ - σ)}\\}. -``` - -It calls [`tracenorm`](@ref) which in turn either uses [`tracenorm_h`](@ref) -or [`tracenorm_nh`](@ref) depending if ``ρ-σ`` is hermitian or not. -""" -tracedistance(rho::DenseOperator, sigma::DenseOperator) = 0.5*tracenorm(rho - sigma) -function tracedistance(rho::T, sigma::T) where T<:Operator - throw(ArgumentError("tracedistance not implemented for $(T). Use dense operators instead.")) -end - -""" - tracedistance_h(rho, sigma) - -Trace distance between `rho` and `sigma`. - -It uses the identity - -```math -T(ρ,σ) = \\frac{1}{2} Tr\\{\\sqrt{(ρ - σ)^† (ρ - σ)}\\} = \\frac{1}{2} \\sum_i |λ_i| -``` - -where ``λ_i`` are the eigenvalues of `rho` - `sigma`. -""" -tracedistance_h(rho::DenseOperator, sigma::DenseOperator) = 0.5*tracenorm_h(rho - sigma) -function tracedistance_h(rho::T, sigma::T) where T<:Operator - throw(ArgumentError("tracedistance_h not implemented for $(T). Use dense operators instead.")) -end - -""" - tracedistance_nh(rho, sigma) - -Trace distance between `rho` and `sigma`. - -Note that in this case `rho` and `sigma` don't have to be represented by square -matrices (i.e. they can have different left-hand and right-hand bases). - -It uses the identity - -```math - T(ρ,σ) = \\frac{1}{2} Tr\\{\\sqrt{(ρ - σ)^† (ρ - σ)}\\} - = \\frac{1}{2} \\sum_i σ_i -``` - -where ``σ_i`` are the singular values of `rho` - `sigma`. -""" -tracedistance_nh(rho::DenseOperator, sigma::DenseOperator) = 0.5*tracenorm_nh(rho - sigma) -function tracedistance_nh(rho::T, sigma::T) where T<:Operator - throw(ArgumentError("tracedistance_nh not implemented for $(T). Use dense operators instead.")) -end - - -""" - entropy_vn(rho) - -Von Neumann entropy of a density matrix. - -The Von Neumann entropy of a density operator is defined as - -```math -S(ρ) = -Tr(ρ \\log(ρ)) = -\\sum_n λ_n\\log(λ_n) -``` - -where ``λ_n`` are the eigenvalues of the density matrix ``ρ``, ``\\log`` is the -natural logarithm and ``\\log(0) ≡ 0``. - -# Arguments -* `rho`: Density operator of which to calculate Von Neumann entropy. -* `tol=1e-15`: Tolerance for rounding errors in the computed eigenvalues. -""" -function entropy_vn(rho::DenseOperator; tol::Float64=1e-15) - evals = eigvals(rho.data) - evals[abs.(evals) .< tol] = 0.0 - sum([d == 0 ? 0 : -d*log(d) for d=evals]) -end -entropy_vn(psi::StateVector; kwargs...) = entropy_vn(dm(psi); kwargs...) - -""" - fidelity(rho, sigma) - -Fidelity of two density operators. - -The fidelity of two density operators ``ρ`` and ``σ`` is defined by - -```math -F(ρ, σ) = Tr\\left(\\sqrt{\\sqrt{ρ}σ\\sqrt{ρ}}\\right), -``` - -where ``\\sqrt{ρ}=\\sum_n\\sqrt{λ_n}|ψ⟩⟨ψ|``. -""" -fidelity(rho::DenseOperator, sigma::DenseOperator) = trace(sqrtm(sqrtm(rho.data)*sigma.data*sqrtm(rho.data))) - - -""" - ptranspose(rho, index) - -Partial transpose of rho with respect to subspace specified by index. -""" -function ptranspose(rho::DenseOperator, index::Int=1) - @assert rho.basis_l == rho.basis_r - @assert typeof(rho.basis_l) == CompositeBasis - - # Define permutation - N = length(rho.basis_l.bases) - perm = [1:N;] - perm[index] = N - perm[N] = index - - # Permute indexed subsystem to last position - rho_perm = permutesystems(rho, perm) - - # Transpose corresponding blocks - m = Int(prod(rho_perm.basis_l.shape[1:N-1])) - n = rho_perm.basis_l.shape[N] - for i=1:n, j=1:n - rho_perm.data[m*(i-1)+1:m*i, m*(j-1)+1:m*j] = transpose(rho_perm.data[m*(i-1)+1:m*i, m*(j-1)+1:m*j]) - end - - return permutesystems(rho_perm, perm) -end - - -""" - PPT(rho, index) - -Peres-Horodecki criterion of partial transpose. -""" -PPT(rho::DenseOperator, index::Int) = all(real.(eigvals(ptranspose(rho, index).data)) .>= 0.0) - - -""" - negativity(rho, index) - -Negativity of rho with respect to subsystem index. - -The negativity of a density matrix ρ is defined as - -```math -N(ρ) = \|ρᵀ\|, -``` -where `ρᵀ` is the partial transpose. -""" -negativity(rho::DenseOperator, index::Int) = 0.5*(tracenorm(ptranspose(rho, index)) - 1.0) - - -""" - logarithmic_negativity(rho, index) - -The logarithmic negativity of a density matrix ρ is defined as - -```math -N(ρ) = \log₂\|ρᵀ\|, -``` -where `ρᵀ` is the partial transpose. -""" -logarithmic_negativity(rho::DenseOperator, index::Int) = log(2, tracenorm(ptranspose(rho, index))) - -end # module diff --git a/src/nlevel.jl b/src/nlevel.jl deleted file mode 100644 index a8b80c5f..00000000 --- a/src/nlevel.jl +++ /dev/null @@ -1,59 +0,0 @@ -module nlevel - -export NLevelBasis, transition, nlevelstate - -import Base: == - -using ..bases, ..states, ..operators, ..operators_sparse - - -""" - NLevelBasis(N) - -Basis for a system consisting of N states. -""" -mutable struct NLevelBasis <: Basis - shape::Vector{Int} - N::Int - function NLevelBasis(N::Int) - if N < 1 - throw(DimensionMismatch()) - end - new([N], N) - end -end - -==(b1::NLevelBasis, b2::NLevelBasis) = b1.N == b2.N - - -""" - transition(b::NLevelBasis, to::Int, from::Int) - -Transition operator ``|\\mathrm{to}⟩⟨\\mathrm{from}|``. -""" -function transition(b::NLevelBasis, to::Int, from::Int) - if to < 1 || b.N < to - throw(BoundsError("'to' index has to be between 1 and b.N")) - end - if from < 1 || b.N < from - throw(BoundsError("'from' index has to be between 1 and b.N")) - end - op = SparseOperator(b) - op.data[to, from] = 1. - op -end - - -""" - nlevelstate(b::NLevelBasis, n::Int) - -State where the system is completely in the n-th level. -""" -function nlevelstate(b::NLevelBasis, n::Int) - if n < 1 || b.N < n - throw(BoundsError("n has to be between 1 and b.N")) - end - basisstate(b, n) -end - -end # module \ No newline at end of file diff --git a/src/operators.jl b/src/operators.jl deleted file mode 100644 index 4cdfadb3..00000000 --- a/src/operators.jl +++ /dev/null @@ -1,298 +0,0 @@ -module operators - -export Operator, length, basis, dagger, ishermitian, tensor, embed, - trace, ptrace, normalize, normalize!, expect, variance, - expm, permutesystems, identityoperator - -import Base: ==, +, -, *, /, length, trace, one, ishermitian, expm, conj, conj! -import ..bases: basis, tensor, ptrace, permutesystems, - samebases, check_samebases, multiplicable -import ..states: dagger, normalize, normalize! - -using Compat -using ..sortedindices, ..bases, ..states - - -""" -Abstract base class for all operators. - -All deriving operator classes have to define the fields -`basis_l` and `basis_r` defining the left and right side bases. - -For fast time evolution also at least the function -`gemv!(alpha, op::Operator, x::Ket, beta, result::Ket)` should be -implemented. Many other generic multiplication functions can be defined in -terms of this function and are provided automatically. -""" -abstract type Operator end - - -# Common error messages -arithmetic_unary_error(funcname, x::Operator) = throw(ArgumentError("$funcname is not defined for this type of operator: $(typeof(x)).\nTry to convert to another operator type first with e.g. full() or sparse().")) -arithmetic_binary_error(funcname, a::Operator, b::Operator) = throw(ArgumentError("$funcname is not defined for this combination of types of operators: $(typeof(a)), $(typeof(b)).\nTry to convert to a common operator type first with e.g. full() or sparse().")) -addnumbererror() = throw(ArgumentError("Can't add or subtract a number and an operator. You probably want 'op + identityoperator(op)*x'.")) - -length(a::Operator) = length(a.basis_l)::Int*length(a.basis_r)::Int -basis(a::Operator) = (check_samebases(a); a.basis_l) - -# Arithmetic operations -+(a::Operator, b::Operator) = arithmetic_binary_error("Addition", a, b) -+(a::Number, b::Operator) = addnumbererror() -+(a::Operator, b::Number) = addnumbererror() - --(a::Operator) = arithmetic_unary_error("Negation", a) --(a::Operator, b::Operator) = arithmetic_binary_error("Subtraction", a, b) --(a::Number, b::Operator) = addnumbererror() --(a::Operator, b::Number) = addnumbererror() - -*(a::Operator, b::Operator) = arithmetic_binary_error("Multiplication", a, b) - - -dagger(a::Operator) = arithmetic_unary_error("Hermitian conjugate", a) - -conj(a::Operator) = arithmetic_unary_error("Complex conjugate", a) -conj!(a::Operator) = conj(a::Operator) - -""" - ishermitian(op::Operator) - -Check if an operator is Hermitian. -""" -ishermitian(op::Operator) = arithmetic_unary_error(ishermitian, op) - - -""" - tensor(x::Operator, y::Operator, z::Operator...) - -Tensor product ``\\hat{x}⊗\\hat{y}⊗\\hat{z}⊗…`` of the given operators. -""" -tensor(a::Operator, b::Operator) = arithmetic_binary_error("Tensor product", a, b) -tensor(op::Operator) = op -tensor(operators::Operator...) = reduce(tensor, operators) - - -""" - embed(basis1[, basis2], indices::Vector, operators::Vector) - -Tensor product of operators where missing indices are filled up with identity operators. -""" -function embed(basis_l::CompositeBasis, basis_r::CompositeBasis, - indices::Vector{Int}, operators::Vector{T}) where T<:Operator - N = length(basis_l.bases) - @assert length(basis_r.bases) == N - @assert length(indices) == length(operators) - sortedindices.check_indices(N, indices) - tensor([i ∈ indices ? operators[findfirst(indices, i)] : identityoperator(T, basis_l.bases[i], basis_r.bases[i]) for i=1:N]...) -end -embed(basis_l::CompositeBasis, basis_r::CompositeBasis, index::Int, op::Operator) = embed(basis_l, basis_r, Int[index], [op]) -embed(basis::CompositeBasis, index::Int, op::Operator) = embed(basis, basis, Int[index], [op]) -embed(basis::CompositeBasis, indices::Vector{Int}, operators::Vector{T}) where {T<:Operator} = embed(basis, basis, indices, operators) - -""" - embed(basis1[, basis2], operators::Dict) - -`operators` is a dictionary `Dict{Vector{Int}, Operator}`. The integer vector -specifies in which subsystems the corresponding operator is defined. -""" -function embed(basis_l::CompositeBasis, basis_r::CompositeBasis, - operators::Dict{Vector{Int}, T}) where T<:Operator - @assert length(basis_l.bases) == length(basis_r.bases) - N = length(basis_l.bases) - if length(operators) == 0 - return identityoperator(T, basis_l, basis_r) - end - indices, operator_list = zip(operators...) - operator_list = [operator_list...;] - indices_flat = [indices...;] - start_indices_flat = [i[1] for i in indices] - complement_indices_flat = Int[i for i=1:N if i ∉ indices_flat] - operators_flat = T[] - if all([minimum(I):maximum(I);]==I for I in indices) - for i in 1:N - if i in complement_indices_flat - push!(operators_flat, identityoperator(T, basis_l.bases[i], basis_r.bases[i])) - elseif i in start_indices_flat - push!(operators_flat, operator_list[findfirst(start_indices_flat, i)]) - end - end - return tensor(operators_flat...) - else - complement_operators = [identityoperator(T, basis_l.bases[i], basis_r.bases[i]) for i in complement_indices_flat] - op = tensor([operator_list; complement_operators]...) - perm = sortperm([indices_flat; complement_indices_flat]) - return permutesystems(op, perm) - end -end -embed(basis_l::CompositeBasis, basis_r::CompositeBasis, operators::Dict{Int, T}; kwargs...) where {T<:Operator} = embed(basis_l, basis_r, Dict([i]=>op_i for (i, op_i) in operators); kwargs...) -embed(basis::CompositeBasis, operators::Dict{Int, T}; kwargs...) where {T<:Operator} = embed(basis, basis, operators; kwargs...) -embed(basis::CompositeBasis, operators::Dict{Vector{Int}, T}; kwargs...) where {T<:Operator} = embed(basis, basis, operators; kwargs...) - - -""" - trace(x::Operator) - -Trace of the given operator. -""" -trace(x::Operator) = arithmetic_unary_error("Trace", x) - -ptrace(a::Operator, index::Vector{Int}) = arithmetic_unary_error("Partial trace", a) - -""" - normalize(op) - -Return the normalized operator so that its `trace(op)` is one. -""" -normalize(op::Operator) = op/trace(op) - -""" - normalize!(op) - -In-place normalization of the given operator so that its `trace(x)` is one. -""" -normalize!(op::Operator) = throw(ArgumentError("normalize! is not defined for this type of operator: $(typeof(op)).\n You may have to fall back to the non-inplace version 'normalize()'.")) - -""" - expect(op, state) - -Expectation value of the given operator `op` for the specified `state`. - -`state` can either be a (density) operator or a ket. -""" - -expect(op::Operator, state::Ket) = dagger(state) * op * state -expect(op::Operator, state::Operator) = trace(op*state) - -""" - expect(index, op, state) - -If an `index` is given, it assumes that `op` is defined in the subsystem specified by this number. -""" -function expect(indices::Vector{Int}, op::Operator, state::Operator) - N = length(state.basis_l.shape) - indices_ = sortedindices.complement(N, indices) - expect(op, ptrace(state, indices_)) -end -function expect(indices::Vector{Int}, op::Operator, state::Ket) - N = length(state.basis.shape) - indices_ = sortedindices.complement(N, indices) - expect(op, ptrace(state, indices_)) -end -expect(index::Int, op::Operator, state) = expect([index], op, state) -expect(op::Operator, states::Vector) = [expect(op, state) for state=states] -expect(indices::Vector{Int}, op::Operator, states::Vector) = [expect(indices, op, state) for state=states] - -""" - variance(op, state) - -Variance of the given operator `op` for the specified `state`. - -`state` can either be a (density) operator or a ket. -""" -function variance(op::Operator, state::Ket) - x = op*state - stateT = dagger(state) - stateT*op*x - (stateT*x)^2 -end -function variance(op::Operator, state::Operator) - expect(op*op, state) - expect(op, state)^2 -end - -""" - variance(index, op, state) - -If an `index` is given, it assumes that `op` is defined in the subsystem specified by this number -""" -function variance(indices::Vector{Int}, op::Operator, state::Operator) - N = length(state.basis_l.shape) - indices_ = sortedindices.complement(N, indices) - variance(op, ptrace(state, indices_)) -end -function variance(indices::Vector{Int}, op::Operator, state::Ket) - N = length(state.basis.shape) - indices_ = sortedindices.complement(N, indices) - variance(op, ptrace(state, indices_)) -end -variance(index::Int, op::Operator, state) = variance([index], op, state) -variance(op::Operator, states::Vector) = [variance(op, state) for state=states] -variance(indices::Vector{Int}, op::Operator, states::Vector) = [variance(indices, op, state) for state=states] - - -""" - expm(op::Operator) - -Operator exponential. -""" -expm(op::Operator) = throw(ArgumentError("expm() is not defined for this type of operator: $(typeof(op)).\nTry to convert to dense operator first with full().")) - -permutesystems(a::Operator, perm::Vector{Int}) = arithmetic_unary_error("Permutations of subsystems", a) - -""" - identityoperator(a::Basis[, b::Basis]) - -Return an identityoperator in the given bases. -""" -identityoperator(::Type{T}, b1::Basis, b2::Basis) where {T<:Operator} = throw(ArgumentError("Identity operator not defined for operator type $T.")) -identityoperator(::Type{T}, b::Basis) where {T<:Operator} = identityoperator(T, b, b) -identityoperator(op::T) where {T<:Operator} = identityoperator(T, op.basis_l, op.basis_r) - -one(b::Basis) = identityoperator(b) -one(op::Operator) = identityoperator(op) - - -# Fast in-place multiplication -""" - gemv!(alpha, a, b, beta, result) - -Fast in-place multiplication of operators with state vectors. It -implements the relation `result = beta*result + alpha*a*b`. -Here, `alpha` and `beta` are complex numbers, while `result` and either `a` -or `b` are state vectors while the other one can be of any operator type. -""" -gemv!() = error("Not Implemented.") - -""" - gemm!(alpha, a, b, beta, result) - -Fast in-place multiplication of of operators with DenseOperators. It -implements the relation `result = beta*result + alpha*a*b`. -Here, `alpha` and `beta` are complex numbers, while `result` and either `a` -or `b` are dense operators while the other one can be of any operator type. -""" -gemm!() = error("Not Implemented.") - - -# Helper functions to check validity of arguments -function check_ptrace_arguments(a::Operator, indices::Vector{Int}) - if !isa(a.basis_l, CompositeBasis) || !isa(a.basis_r, CompositeBasis) - throw(ArgumentError("Partial trace can only be applied onto operators with composite bases.")) - end - rank = length(a.basis_l.shape) - if rank != length(a.basis_r.shape) - throw(ArgumentError("Partial trace can only be applied onto operators wich have the same number of subsystems in the left basis and right basis.")) - end - if rank == length(indices) - throw(ArgumentError("Partial trace can't be used to trace out all subsystems - use trace() instead.")) - end - sortedindices.check_indices(length(a.basis_l.shape), indices) - for i=indices - if a.basis_l.shape[i] != a.basis_r.shape[i] - throw(ArgumentError("Partial trace can only be applied onto subsystems that have the same left and right dimension.")) - end - end -end -function check_ptrace_arguments(a::StateVector, indices::Vector{Int}) - if length(basis(a).shape) == length(indices) - throw(ArgumentError("Partial trace can't be used to trace out all subsystems - use trace() instead.")) - end - sortedindices.check_indices(length(basis(a).shape), indices) -end - -samebases(a::Operator) = samebases(a.basis_l, a.basis_r)::Bool -samebases(a::Operator, b::Operator) = samebases(a.basis_l, b.basis_l)::Bool && samebases(a.basis_r, b.basis_r)::Bool -check_samebases(a::Operator) = check_samebases(a.basis_l, a.basis_r) - -multiplicable(a::Operator, b::Ket) = multiplicable(a.basis_r, b.basis) -multiplicable(a::Bra, b::Operator) = multiplicable(a.basis, b.basis_l) -multiplicable(a::Operator, b::Operator) = multiplicable(a.basis_r, b.basis_l) - -end # module diff --git a/src/operators_dense.jl b/src/operators_dense.jl deleted file mode 100644 index b964bbbb..00000000 --- a/src/operators_dense.jl +++ /dev/null @@ -1,295 +0,0 @@ -module operators_dense - -export DenseOperator, full, projector, dm - -import Base: ==, +, -, *, / -import ..operators - -using Base.LinAlg, Base.Cartesian -using ..bases, ..states, ..operators - - -""" - DenseOperator(b1[, b2, data]) - -Dense array implementation of Operator. - -The matrix consisting of complex floats is stored in the `data` field. -""" -mutable struct DenseOperator <: Operator - basis_l::Basis - basis_r::Basis - data::Matrix{Complex128} - DenseOperator(b1::Basis, b2::Basis, data) = length(b1) == size(data, 1) && length(b2) == size(data, 2) ? new(b1, b2, data) : throw(DimensionMismatch()) -end - -DenseOperator(b::Basis, data) = DenseOperator(b, b, data) -DenseOperator(b1::Basis, b2::Basis) = DenseOperator(b1, b2, zeros(Complex128, length(b1), length(b2))) -DenseOperator(b::Basis) = DenseOperator(b, b) -DenseOperator(op::Operator) = full(op) - -Base.copy(x::DenseOperator) = DenseOperator(x.basis_l, x.basis_r, copy(x.data)) - -""" - full(op::Operator) - -Convert an arbitrary Operator into a [`DenseOperator`](@ref). -""" -Base.full(x::Operator) = throw(ArgumentError("Conversion from $(typeof(x)) to a DenseOperator not implemented.")) -Base.full(x::DenseOperator) = copy(x) - -==(x::DenseOperator, y::DenseOperator) = (x.basis_l == y.basis_l) && (x.basis_r == y.basis_r) && (x.data == y.data) - - -# Arithmetic operations -+(a::DenseOperator, b::DenseOperator) = (check_samebases(a,b); DenseOperator(a.basis_l, a.basis_r, a.data+b.data)) - --(a::DenseOperator) = DenseOperator(a.basis_l, a.basis_r, -a.data) --(a::DenseOperator, b::DenseOperator) = (check_samebases(a,b); DenseOperator(a.basis_l, a.basis_r, a.data-b.data)) - -*(a::DenseOperator, b::Ket) = (check_multiplicable(a, b); Ket(a.basis_l, a.data*b.data)) -*(a::Bra, b::DenseOperator) = (check_multiplicable(a, b); Bra(b.basis_r, b.data.'*a.data)) -*(a::DenseOperator, b::DenseOperator) = (check_multiplicable(a, b); DenseOperator(a.basis_l, b.basis_r, a.data*b.data)) -*(a::DenseOperator, b::Number) = DenseOperator(a.basis_l, a.basis_r, complex(b)*a.data) -*(a::Number, b::DenseOperator) = DenseOperator(b.basis_l, b.basis_r, complex(a)*b.data) -function *(op1::Operator, op2::DenseOperator) - check_multiplicable(op1, op2) - result = DenseOperator(op1.basis_l, op2.basis_r) - operators.gemm!(Complex(1.), op1, op2, Complex(0.), result) - return result -end -function *(op1::DenseOperator, op2::Operator) - check_multiplicable(op1, op2) - result = DenseOperator(op1.basis_l, op2.basis_r) - operators.gemm!(Complex(1.), op1, op2, Complex(0.), result) - return result -end -function *(op::Operator, psi::Ket) - check_multiplicable(op, psi) - result = Ket(op.basis_l) - operators.gemv!(Complex(1.), op, psi, Complex(0.), result) - return result -end -function *(psi::Bra, op::Operator) - check_multiplicable(psi, op) - result = Bra(op.basis_r) - operators.gemv!(Complex(1.), psi, op, Complex(0.), result) - return result -end - -/(a::DenseOperator, b::Number) = DenseOperator(a.basis_l, a.basis_r, a.data/complex(b)) - - -operators.dagger(x::DenseOperator) = DenseOperator(x.basis_r, x.basis_l, x.data') - -operators.ishermitian(A::DenseOperator) = ishermitian(A.data) - -operators.tensor(a::DenseOperator, b::DenseOperator) = DenseOperator(tensor(a.basis_l, b.basis_l), tensor(a.basis_r, b.basis_r), kron(b.data, a.data)) - -operators.conj(a::DenseOperator) = DenseOperator(a.basis_l, a.basis_r, conj(a.data)) -operators.conj!(a::DenseOperator) = conj!(a.data) - -""" - tensor(x::Ket, y::Bra) - -Outer product ``|x⟩⟨y|`` of the given states. -""" -operators.tensor(a::Ket, b::Bra) = DenseOperator(a.basis, b.basis, reshape(kron(b.data, a.data), prod(a.basis.shape), prod(b.basis.shape))) - - -operators.trace(op::DenseOperator) = (check_samebases(op); trace(op.data)) - -function operators.ptrace(a::DenseOperator, indices::Vector{Int}) - operators.check_ptrace_arguments(a, indices) - rank = length(a.basis_l.shape) - result = _ptrace(Val{rank}, a.data, a.basis_l.shape, a.basis_r.shape, indices) - return DenseOperator(ptrace(a.basis_l, indices), ptrace(a.basis_r, indices), result) -end - -function operators.ptrace(psi::Ket, indices::Vector{Int}) - operators.check_ptrace_arguments(psi, indices) - b = basis(psi) - b_ = ptrace(b, indices) - rank = length(b.shape) - result = _ptrace_ket(Val{rank}, psi.data, b.shape, indices) - return DenseOperator(b_, b_, result) -end -function operators.ptrace(psi::Bra, indices::Vector{Int}) - operators.check_ptrace_arguments(psi, indices) - b = basis(psi) - b_ = ptrace(b, indices) - rank = length(b.shape) - result = _ptrace_bra(Val{rank}, psi.data, b.shape, indices) - return DenseOperator(b_, b_, result) -end - -operators.normalize!(op::DenseOperator) = scale!(op.data, 1./trace(op)) - -function operators.expect(op::DenseOperator, state::Ket)# where T <: Union{Ket, Bra} - check_samebases(op.basis_r, state.basis) - check_samebases(op.basis_l, state.basis) - state.data' * op.data * state.data -end - -function operators.expect(op::DenseOperator, state::Operator) - check_samebases(op.basis_r, state.basis_l) - check_samebases(op.basis_l, state.basis_r) - result = Complex128(0.) - @inbounds for i=1:size(op.data, 1), j=1:size(op.data,2) - result += op.data[i,j]*state.data[j,i] - end - result -end - -function operators.expm(op::DenseOperator) - check_samebases(op) - return DenseOperator(op.basis_l, op.basis_r, expm(op.data)) -end - -function operators.permutesystems(a::DenseOperator, perm::Vector{Int}) - @assert length(a.basis_l.bases) == length(a.basis_r.bases) == length(perm) - @assert isperm(perm) - data = reshape(a.data, [a.basis_l.shape; a.basis_r.shape]...) - data = permutedims(data, [perm; perm + length(perm)]) - data = reshape(data, length(a.basis_l), length(a.basis_r)) - DenseOperator(permutesystems(a.basis_l, perm), permutesystems(a.basis_r, perm), data) -end - -operators.identityoperator(::Type{DenseOperator}, b1::Basis, b2::Basis) = DenseOperator(b1, b2, eye(Complex128, length(b1), length(b2))) - -""" - projector(a::Ket, b::Bra) - -Projection operator ``|a⟩⟨b|``. -""" -projector(a::Ket, b::Bra) = tensor(a, b) -""" - projector(a::Ket) - -Projection operator ``|a⟩⟨a|``. -""" -projector(a::Ket) = tensor(a, dagger(a)) -""" - projector(a::Bra) - -Projection operator ``|a⟩⟨a|``. -""" -projector(a::Bra) = tensor(dagger(a), a) - -""" - dm(a::StateVector) - -Create density matrix ``|a⟩⟨a|``. Same as `projector(a)`. -""" -dm(x::Ket) = tensor(x, dagger(x)) -dm(x::Bra) = tensor(dagger(x), x) - - -# Partial trace implementation for dense operators. -function _strides(shape::Vector{Int}) - N = length(shape) - S = zeros(Int, N) - S[1] = 1 - for m=2:N - S[m] = S[m-1]*shape[m-1] - end - return S -end - -# Dense operator version -@generated function _ptrace(::Type{Val{RANK}}, a::Matrix{Complex128}, - shape_l::Vector{Int}, shape_r::Vector{Int}, - indices::Vector{Int}) where RANK - return quote - a_strides_l = _strides(shape_l) - result_shape_l = copy(shape_l) - result_shape_l[indices] = 1 - result_strides_l = _strides(result_shape_l) - a_strides_r = _strides(shape_r) - result_shape_r = copy(shape_r) - result_shape_r[indices] = 1 - result_strides_r = _strides(result_shape_r) - N_result_l = prod(result_shape_l) - N_result_r = prod(result_shape_r) - result = zeros(Complex128, N_result_l, N_result_r) - @nexprs 1 (d->(Jr_{$RANK}=1;Ir_{$RANK}=1)) - @nloops $RANK ir (d->1:shape_r[d]) (d->(Ir_{d-1}=Ir_d; Jr_{d-1}=Jr_d)) (d->(Ir_d+=a_strides_r[d]; if !(d in indices) Jr_d+=result_strides_r[d] end)) begin - @nexprs 1 (d->(Jl_{$RANK}=1;Il_{$RANK}=1)) - @nloops $RANK il (k->1:shape_l[k]) (k->(Il_{k-1}=Il_k; Jl_{k-1}=Jl_k; if (k in indices && il_k!=ir_k) Il_k+=a_strides_l[k]; continue end)) (k->(Il_k+=a_strides_l[k]; if !(k in indices) Jl_k+=result_strides_l[k] end)) begin - result[Jl_0, Jr_0] += a[Il_0, Ir_0] - end - end - return result - end -end - -@generated function _ptrace_ket(::Type{Val{RANK}}, a::Vector{Complex128}, - shape::Vector{Int}, indices::Vector{Int}) where RANK - return quote - a_strides = _strides(shape) - result_shape = copy(shape) - result_shape[indices] = 1 - result_strides = _strides(result_shape) - N_result = prod(result_shape) - result = zeros(Complex128, N_result, N_result) - @nexprs 1 (d->(Jr_{$RANK}=1;Ir_{$RANK}=1)) - @nloops $RANK ir (d->1:shape[d]) (d->(Ir_{d-1}=Ir_d; Jr_{d-1}=Jr_d)) (d->(Ir_d+=a_strides[d]; if !(d in indices) Jr_d+=result_strides[d] end)) begin - @nexprs 1 (d->(Jl_{$RANK}=1;Il_{$RANK}=1)) - @nloops $RANK il (k->1:shape[k]) (k->(Il_{k-1}=Il_k; Jl_{k-1}=Jl_k; if (k in indices && il_k!=ir_k) Il_k+=a_strides[k]; continue end)) (k->(Il_k+=a_strides[k]; if !(k in indices) Jl_k+=result_strides[k] end)) begin - result[Jl_0, Jr_0] += a[Il_0]*conj(a[Ir_0]) - end - end - return result - end -end - -@generated function _ptrace_bra(::Type{Val{RANK}}, a::Vector{Complex128}, - shape::Vector{Int}, indices::Vector{Int}) where RANK - return quote - a_strides = _strides(shape) - result_shape = copy(shape) - result_shape[indices] = 1 - result_strides = _strides(result_shape) - N_result = prod(result_shape) - result = zeros(Complex128, N_result, N_result) - @nexprs 1 (d->(Jr_{$RANK}=1;Ir_{$RANK}=1)) - @nloops $RANK ir (d->1:shape[d]) (d->(Ir_{d-1}=Ir_d; Jr_{d-1}=Jr_d)) (d->(Ir_d+=a_strides[d]; if !(d in indices) Jr_d+=result_strides[d] end)) begin - @nexprs 1 (d->(Jl_{$RANK}=1;Il_{$RANK}=1)) - @nloops $RANK il (k->1:shape[k]) (k->(Il_{k-1}=Il_k; Jl_{k-1}=Jl_k; if (k in indices && il_k!=ir_k) Il_k+=a_strides[k]; continue end)) (k->(Il_k+=a_strides[k]; if !(k in indices) Jl_k+=result_strides[k] end)) begin - result[Jl_0, Jr_0] += conj(a[Il_0])*a[Ir_0] - end - end - return result - end -end - -# Fast in-place multiplication -operators.gemm!(alpha, a::Matrix{Complex128}, b::Matrix{Complex128}, beta, result::Matrix{Complex128}) = BLAS.gemm!('N', 'N', convert(Complex128, alpha), a, b, convert(Complex128, beta), result) -operators.gemv!(alpha, a::Matrix{Complex128}, b::Vector{Complex128}, beta, result::Vector{Complex128}) = BLAS.gemv!('N', convert(Complex128, alpha), a, b, convert(Complex128, beta), result) -operators.gemv!(alpha, a::Vector{Complex128}, b::Matrix{Complex128}, beta, result::Vector{Complex128}) = BLAS.gemv!('T', convert(Complex128, alpha), b, a, convert(Complex128, beta), result) - -operators.gemm!(alpha, a::DenseOperator, b::DenseOperator, beta, result::DenseOperator) = operators.gemm!(convert(Complex128, alpha), a.data, b.data, convert(Complex128, beta), result.data) -operators.gemv!(alpha, a::DenseOperator, b::Ket, beta, result::Ket) = operators.gemv!(convert(Complex128, alpha), a.data, b.data, convert(Complex128, beta), result.data) -operators.gemv!(alpha, a::Bra, b::DenseOperator, beta, result::Bra) = operators.gemv!(convert(Complex128, alpha), a.data, b.data, convert(Complex128, beta), result.data) - - -# Multiplication for Operators in terms of their gemv! implementation -function operators.gemm!(alpha, M::Operator, b::DenseOperator, beta, result::DenseOperator) - for i=1:size(b.data, 2) - bket = Ket(b.basis_l, b.data[:,i]) - resultket = Ket(M.basis_l, result.data[:,i]) - operators.gemv!(alpha, M, bket, beta, resultket) - result.data[:,i] = resultket.data - end -end - -function operators.gemm!(alpha, b::DenseOperator, M::Operator, beta, result::DenseOperator) - for i=1:size(b.data, 1) - bbra = Bra(b.basis_r, vec(b.data[i,:])) - resultbra = Bra(M.basis_r, vec(result.data[i,:])) - operators.gemv!(alpha, bbra, M, beta, resultbra) - result.data[i,:] = resultbra.data - end -end - -end # module diff --git a/src/operators_lazyproduct.jl b/src/operators_lazyproduct.jl deleted file mode 100644 index 4aece764..00000000 --- a/src/operators_lazyproduct.jl +++ /dev/null @@ -1,92 +0,0 @@ -module operators_lazyproduct - -export LazyProduct - -import Base: ==, *, /, +, - -import ..operators - -using ..bases, ..states, ..operators, ..operators_dense - - -""" - LazyProduct(operators[, factor=1]) - LazyProduct(op1, op2...) - -Lazy evaluation of products of operators. - -The factors of the product are stored in the `operators` field. Additionally a -complex factor is stored in the `factor` field which allows for fast -multiplication with numbers. -""" -mutable struct LazyProduct <: Operator - basis_l::Basis - basis_r::Basis - factor::Complex128 - operators::Vector{Operator} - - function LazyProduct(operators::Vector{Operator}, factor::Number=1) - if length(operators) < 1 - throw(ArgumentError("LazyProduct needs at least one operator.")) - end - for i = 2:length(operators) - check_multiplicable(operators[i-1], operators[i]) - end - new(operators[1].basis_l, operators[end].basis_r, factor, operators) - end -end -LazyProduct(operators::Vector, factor::Number=1) = LazyProduct(convert(Vector{Operator}, operators), factor) -LazyProduct(operators::Operator...) = LazyProduct(Operator[operators...]) - -Base.copy(x::LazyProduct) = LazyProduct([copy(op) for op in x.operators], x.factor) - -Base.full(op::LazyProduct) = op.factor*prod(full.(op.operators)) -Base.sparse(op::LazyProduct) = op.factor*prod(sparse.(op.operators)) - -==(x::LazyProduct, y::LazyProduct) = (x.basis_l == y.basis_l) && (x.basis_r == y.basis_r) && x.operators==y.operators && x.factor == y.factor - - -# Arithmetic operations --(a::LazyProduct) = LazyProduct(a.operators, -a.factor) - -*(a::LazyProduct, b::LazyProduct) = (check_multiplicable(a, b); LazyProduct([a.operators; b.operators], a.factor*b.factor)) -*(a::LazyProduct, b::Number) = LazyProduct(a.operators, a.factor*b) -*(a::Number, b::LazyProduct) = LazyProduct(b.operators, a*b.factor) - -/(a::LazyProduct, b::Number) = LazyProduct(a.operators, a.factor/b) - - -operators.dagger(op::LazyProduct) = LazyProduct(dagger.(reverse(op.operators)), conj(op.factor)) - -operators.trace(op::LazyProduct) = throw(ArgumentError("Trace of LazyProduct is not defined. Try to convert to another operator type first with e.g. full() or sparse().")) - -operators.ptrace(op::LazyProduct, indices::Vector{Int}) = throw(ArgumentError("Partial trace of LazyProduct is not defined. Try to convert to another operator type first with e.g. full() or sparse().")) - -operators.permutesystems(op::LazyProduct, perm::Vector{Int}) = LazyProduct(Operator[permutesystems(op_i, perm) for op_i in op.operators], op.factor) - -operators.identityoperator(::Type{LazyProduct}, b1::Basis, b2::Basis) = LazyProduct(identityoperator(b1, b2)) - - -# Fast in-place multiplication -function operators.gemv!(alpha, a::LazyProduct, b::Ket, beta, result::Ket) - tmp1 = Ket(a.operators[end].basis_l) - operators.gemv!(a.factor, a.operators[end], b, 0, tmp1) - for i=length(a.operators)-1:-1:2 - tmp2 = Ket(a.operators[i].basis_l) - operators.gemv!(1, a.operators[i], tmp1, 0, tmp2) - tmp1 = tmp2 - end - operators.gemv!(alpha, a.operators[1], tmp1, beta, result) -end - -function operators.gemv!(alpha, a::Bra, b::LazyProduct, beta, result::Bra) - tmp1 = Bra(b.operators[1].basis_r) - operators.gemv!(b.factor, a, b.operators[1], 0, tmp1) - for i=2:length(b.operators)-1 - tmp2 = Bra(b.operators[i].basis_r) - operators.gemv!(1, tmp1, b.operators[i], 0, tmp2) - tmp1 = tmp2 - end - operators.gemv!(alpha, tmp1, b.operators[end], beta, result) -end - -end # module diff --git a/src/operators_lazysum.jl b/src/operators_lazysum.jl deleted file mode 100644 index a7965dde..00000000 --- a/src/operators_lazysum.jl +++ /dev/null @@ -1,90 +0,0 @@ -module operators_lazysum - -export LazySum - -import Base: ==, *, /, +, - -import ..operators - -using ..bases, ..states, ..operators, ..operators_dense - - -""" - LazySum([factors,] operators) - -Lazy evaluation of sums of operators. - -All operators have to be given in respect to the same bases. The field -`factors` accounts for an additional multiplicative factor for each operator -stored in the `operators` field. -""" -mutable struct LazySum <: Operator - basis_l::Basis - basis_r::Basis - factors::Vector{Complex128} - operators::Vector{Operator} - - function LazySum(factors::Vector{Complex128}, operators::Vector{Operator}) - @assert length(operators)>0 - @assert length(operators)==length(factors) - for i = 2:length(operators) - @assert operators[1].basis_l == operators[i].basis_l - @assert operators[1].basis_r == operators[i].basis_r - end - new(operators[1].basis_l, operators[1].basis_r, factors, operators) - end -end -LazySum(factors::Vector{T}, operators::Vector) where {T<:Number} = LazySum(complex(factors), Operator[op for op in operators]) -LazySum(operators::Operator...) = LazySum(ones(Complex128, length(operators)), Operator[operators...]) - -Base.copy(x::LazySum) = LazySum(copy(x.factors), [copy(op) for op in x.operators]) - -Base.full(op::LazySum) = sum(op.factors .* full.(op.operators)) -Base.sparse(op::LazySum) = sum(op.factors .* sparse.(op.operators)) - -==(x::LazySum, y::LazySum) = (x.basis_l == y.basis_l) && (x.basis_r == y.basis_r) && x.operators==y.operators && x.factors==y.factors - -# Arithmetic operations -+(a::LazySum, b::LazySum) = (check_samebases(a,b); LazySum([a.factors; b.factors], [a.operators; b.operators])) - --(a::LazySum) = LazySum(-a.factors, a.operators) --(a::LazySum, b::LazySum) = (check_samebases(a,b); LazySum([a.factors; -b.factors], [a.operators; b.operators])) - -*(a::LazySum, b::Number) = LazySum(b*a.factors, a.operators) -*(a::Number, b::LazySum) = LazySum(a*b.factors, b.operators) - -/(a::LazySum, b::Number) = LazySum(a.factors/b, a.operators) - -operators.dagger(op::LazySum) = LazySum(conj.(op.factors), dagger.(op.operators)) - -operators.trace(op::LazySum) = sum(op.factors .* trace.(op.operators)) - -function operators.ptrace(op::LazySum, indices::Vector{Int}) - operators.check_ptrace_arguments(op, indices) - rank = length(op.basis_l.shape) - length(indices) - D = Operator[ptrace(op_i, indices) for op_i in op.operators] - LazySum(op.factors, D) -end - -operators.normalize!(op::LazySum) = (op.factors /= trace(op)) - -operators.permutesystems(op::LazySum, perm::Vector{Int}) = LazySum(op.factors, Operator[permutesystems(op_i, perm) for op_i in op.operators]) - -operators.identityoperator(::Type{LazySum}, b1::Basis, b2::Basis) = LazySum(identityoperator(b1, b2)) - - -# Fast in-place multiplication -function operators.gemv!(alpha, a::LazySum, b::Ket, beta, result::Ket) - operators.gemv!(alpha*a.factors[1], a.operators[1], b, beta, result) - for i=2:length(a.operators) - operators.gemv!(alpha*a.factors[i], a.operators[i], b, 1, result) - end -end - -function operators.gemv!(alpha, a::Bra, b::LazySum, beta, result::Bra) - operators.gemv!(alpha*b.factors[1], a, b.operators[1], beta, result) - for i=2:length(b.operators) - operators.gemv!(alpha*b.factors[i], a, b.operators[i], 1, result) - end -end - -end # module diff --git a/src/operators_lazytensor.jl b/src/operators_lazytensor.jl deleted file mode 100644 index ca51ee9b..00000000 --- a/src/operators_lazytensor.jl +++ /dev/null @@ -1,327 +0,0 @@ -module operators_lazytensor - -export LazyTensor - -import Base: ==, *, /, +, - -import ..operators - -using ..sortedindices, ..bases, ..states, ..operators -using ..operators_dense, ..operators_sparse - - -""" - LazyTensor(b1[, b2], indices, operators[, factor=1]) - -Lazy implementation of a tensor product of operators. - -The suboperators are stored in the `operators` field. The `indices` field -specifies in which subsystem the corresponding operator lives. Additionally, -a complex factor is stored in the `factor` field which allows for fast -multiplication with numbers. -""" -mutable struct LazyTensor <: Operator - basis_l::CompositeBasis - basis_r::CompositeBasis - factor::Complex128 - indices::Vector{Int} - operators::Vector{Operator} - - function LazyTensor(op::LazyTensor, factor::Number) - new(op.basis_l, op.basis_r, factor, op.indices, op.operators) - end - - function LazyTensor(basis_l::Basis, basis_r::Basis, - indices::Vector{Int}, ops::Vector, - factor::Number=1) - if typeof(basis_l) != CompositeBasis - basis_l = CompositeBasis(basis_l.shape, Basis[basis_l]) - end - if typeof(basis_r) != CompositeBasis - basis_r = CompositeBasis(basis_r.shape, Basis[basis_r]) - end - N = length(basis_l.bases) - @assert N==length(basis_r.bases) - sortedindices.check_indices(N, indices) - @assert length(indices) == length(ops) - for n=1:length(indices) - @assert isa(ops[n], Operator) - @assert ops[n].basis_l == basis_l.bases[indices[n]] - @assert ops[n].basis_r == basis_r.bases[indices[n]] - end - if !issorted(indices) - perm = sortperm(indices) - indices = indices[perm] - ops = ops[perm] - end - new(basis_l, basis_r, complex(factor), indices, ops) - end -end - -LazyTensor(basis::Basis, indices::Vector{Int}, ops::Vector, factor::Number=1) = LazyTensor(basis, basis, indices, ops, factor) -LazyTensor(basis_l::Basis, basis_r::Basis, index::Int, operator::Operator, factor::Number=1) = LazyTensor(basis_l, basis_r, [index], Operator[operator], factor) -LazyTensor(basis::Basis, index::Int, operators::Operator, factor::Number=1.) = LazyTensor(basis, basis, index, operators, factor) - -Base.copy(x::LazyTensor) = LazyTensor(x.basis_l, x.basis_r, copy(x.indices), [copy(op) for op in x.operators], x.factor) - -""" - suboperator(op::LazyTensor, index) - -Return the suboperator corresponding to the subsystem specified by `index`. Fails -if there is no corresponding operator (i.e. it would be an identity operater). -""" -suboperator(op::LazyTensor, index::Int) = op.operators[findfirst(op.indices, index)] -""" - suboperators(op::LazyTensor, index) - -Return the suboperators corresponding to the subsystems specified by `indices`. Fails -if there is no corresponding operator (i.e. it would be an identity operater). -""" -suboperators(op::LazyTensor, indices::Vector{Int}) = op.operators[[findfirst(op.indices, i) for i in indices]] - -Base.full(op::LazyTensor) = op.factor*embed(op.basis_l, op.basis_r, op.indices, DenseOperator[full(x) for x in op.operators]) -Base.sparse(op::LazyTensor) = op.factor*embed(op.basis_l, op.basis_r, op.indices, SparseOperator[sparse(x) for x in op.operators]) - -==(x::LazyTensor, y::LazyTensor) = (x.basis_l == y.basis_l) && (x.basis_r == y.basis_r) && x.operators==y.operators && x.factor==y.factor - - -# Arithmetic operations --(a::LazyTensor) = LazyTensor(a, -a.factor) - -function *(a::LazyTensor, b::LazyTensor) - check_multiplicable(a, b) - indices = sortedindices.union(a.indices, b.indices) - ops = Vector{Operator}(length(indices)) - for n in 1:length(indices) - i = indices[n] - in_a = i in a.indices - in_b = i in b.indices - if in_a && in_b - ops[n] = suboperator(a, i)*suboperator(b, i) - elseif in_a - a_i = suboperator(a, i) - ops[n] = a_i*identityoperator(typeof(a_i), b.basis_l.bases[i], b.basis_r.bases[i]) - elseif in_b - b_i = suboperator(b, i) - ops[n] = identityoperator(typeof(b_i), a.basis_l.bases[i], a.basis_r.bases[i])*b_i - end - end - return LazyTensor(a.basis_l, b.basis_r, indices, ops, a.factor*b.factor) -end -*(a::LazyTensor, b::Number) = LazyTensor(a, a.factor*b) -*(a::Number, b::LazyTensor) = LazyTensor(b, a*b.factor) -function *(a::LazyTensor, b::DenseOperator) - check_multiplicable(a, b) - result = DenseOperator(a.basis_l, b.basis_r) - operators.gemm!(complex(1.), a, b, complex(1.), result) - result -end -function *(a::DenseOperator, b::LazyTensor) - check_multiplicable(a, b) - result = DenseOperator(a.basis_l, b.basis_r) - operators.gemm!(complex(1.), a, b, complex(1.), result) - result -end - -/(a::LazyTensor, b::Number) = LazyTensor(a, a.factor/b) - - -operators.dagger(op::LazyTensor) = LazyTensor(op.basis_r, op.basis_l, op.indices, Operator[dagger(x) for x in op.operators], conj(op.factor)) - -operators.tensor(a::LazyTensor, b::LazyTensor) = LazyTensor(a.basis_l ⊗ b.basis_l, a.basis_r ⊗ b.basis_r, [a.indices; b.indices+length(a.basis_l.bases)], Operator[a.operators; b.operators], a.factor*b.factor) - -function operators.trace(op::LazyTensor) - b = basis(op) - result = op.factor - for i in 1:length(b.bases) - if i in op.indices - result *= trace(suboperator(op, i)) - else - result *= length(b.bases[i]) - end - end - result -end - -function operators.ptrace(op::LazyTensor, indices::Vector{Int}) - operators.check_ptrace_arguments(op, indices) - N = length(op.basis_l.shape) - rank = N - length(indices) - factor = op.factor - for i in indices - if i in op.indices - factor *= trace(suboperator(op, i)) - else - factor *= length(op.basis_l.bases[i]) - end - end - remaining_indices = sortedindices.remove(op.indices, indices) - if rank==1 && length(remaining_indices)==1 - return factor * suboperator(op, remaining_indices[1]) - end - b_l = ptrace(op.basis_l, indices) - b_r = ptrace(op.basis_r, indices) - if rank==1 - return factor * identityoperator(b_l, b_r) - end - ops = Vector{Operator}(length(remaining_indices)) - for i in 1:length(ops) - ops[i] = suboperator(op, remaining_indices[i]) - end - LazyTensor(b_l, b_r, sortedindices.shiftremove(op.indices, indices), ops, factor) -end - -operators.normalize!(op::LazyTensor) = (op.factor /= trace(op)) - -function operators.permutesystems(op::LazyTensor, perm::Vector{Int}) - b_l = permutesystems(op.basis_l, perm) - b_r = permutesystems(op.basis_r, perm) - indices = [findfirst(perm, i) for i in op.indices] - perm_ = sortperm(indices) - LazyTensor(b_l, b_r, indices[perm_], op.operators[perm_], op.factor) -end - -operators.identityoperator(::Type{LazyTensor}, b1::Basis, b2::Basis) = LazyTensor(b1, b2, Int[], Operator[]) - - -# Recursively calculate result_{IK} = \\sum_J op_{IJ} h_{JK} -function _gemm_recursive_dense_lazy(i_k::Int, N_k::Int, K::Int, J::Int, val::Complex128, - shape::Vector{Int}, strides_k::Vector{Int}, strides_j::Vector{Int}, - indices::Vector{Int}, h::LazyTensor, - op::Matrix{Complex128}, result::Matrix{Complex128}) - if i_k > N_k - for I=1:size(op, 1) - result[I, K] += val*op[I, J] - end - return nothing - end - if i_k in indices - h_i = operators_lazytensor.suboperator(h, i_k) - if isa(h_i, SparseOperator) - h_i_data = h_i.data::SparseMatrixCSC{Complex128,Int} - @inbounds for k=1:h_i_data.n - K_ = K + strides_k[i_k]*(k-1) - @inbounds for jptr=h_i_data.colptr[k]:h_i_data.colptr[k+1]-1 - j = h_i_data.rowval[jptr] - J_ = J + strides_j[i_k]*(j-1) - val_ = val*h_i_data.nzval[jptr] - _gemm_recursive_dense_lazy(i_k+1, N_k, K_, J_, val_, shape, strides_k, strides_j, indices, h, op, result) - end - end - elseif isa(h_i, DenseOperator) - h_i_data = h_i.data::Matrix{Complex128} - Nk = size(h_i_data, 2) - Nj = size(h_i_data, 1) - @inbounds for k=1:Nk - K_ = K + strides_k[i_k]*(k-1) - @inbounds for j=1:Nj - J_ = J + strides_j[i_k]*(j-1) - val_ = val*h_i_data[j,k] - _gemm_recursive_dense_lazy(i_k+1, N_k, K_, J_, val_, shape, strides_k, strides_j, indices, h, op, result) - end - end - else - throw(ArgumentError("gemm! of LazyTensor is not implemented for $(typeof(h_i))")) - end - else - @inbounds for k=1:shape[i_k] - K_ = K + strides_k[i_k]*(k-1) - J_ = J + strides_j[i_k]*(k-1) - _gemm_recursive_dense_lazy(i_k + 1, N_k, K_, J_, val, shape, strides_k, strides_j, indices, h, op, result) - end - end -end - - -# Recursively calculate result_{JI} = \\sum_K h_{JK} op_{KI} -function _gemm_recursive_lazy_dense(i_k::Int, N_k::Int, K::Int, J::Int, val::Complex128, - shape::Vector{Int}, strides_k::Vector{Int}, strides_j::Vector{Int}, - indices::Vector{Int}, h::LazyTensor, - op::Matrix{Complex128}, result::Matrix{Complex128}) - if i_k > N_k - for I=1:size(op, 2) - result[J, I] += val*op[K, I] - end - return nothing - end - if i_k in indices - h_i = suboperator(h, i_k) - if isa(h_i, SparseOperator) - h_i_data = h_i.data::SparseMatrixCSC{Complex128,Int} - @inbounds for k=1:h_i_data.n - K_ = K + strides_k[i_k]*(k-1) - @inbounds for jptr=h_i_data.colptr[k]:h_i_data.colptr[k+1]-1 - j = h_i_data.rowval[jptr] - J_ = J + strides_j[i_k]*(j-1) - val_ = val*h_i_data.nzval[jptr] - _gemm_recursive_lazy_dense(i_k+1, N_k, K_, J_, val_, shape, strides_k, strides_j, indices, h, op, result) - end - end - elseif isa(h_i, DenseOperator) - h_i_data = h_i.data::Matrix{Complex128} - Nk = size(h_i_data, 2) - Nj = size(h_i_data, 1) - @inbounds for k=1:Nk - K_ = K + strides_k[i_k]*(k-1) - @inbounds for j=1:Nj - J_ = J + strides_j[i_k]*(j-1) - val_ = val*h_i_data[j,k] - _gemm_recursive_lazy_dense(i_k+1, N_k, K_, J_, val_, shape, strides_k, strides_j, indices, h, op, result) - end - end - else - throw(ArgumentError("gemm! of LazyTensor is not implemented for $(typeof(h_i))")) - end - else - @inbounds for k=1:shape[i_k] - K_ = K + strides_k[i_k]*(k-1) - J_ = J + strides_j[i_k]*(k-1) - _gemm_recursive_lazy_dense(i_k + 1, N_k, K_, J_, val, shape, strides_k, strides_j, indices, h, op, result) - end - end -end - -function gemm(alpha::Complex128, op::Matrix{Complex128}, h::LazyTensor, beta::Complex128, result::Matrix{Complex128}) - if beta == Complex128(0.) - fill!(result, beta) - elseif beta != Complex128(1.) - scale!(beta, result) - end - N_k = length(h.basis_r.bases) - shape = [min(h.basis_l.shape[i], h.basis_r.shape[i]) for i=1:length(h.basis_l.shape)] - strides_j = operators_dense._strides(h.basis_l.shape) - strides_k = operators_dense._strides(h.basis_r.shape) - _gemm_recursive_dense_lazy(1, N_k, 1, 1, alpha*h.factor, shape, strides_k, strides_j, h.indices, h, op, result) -end - -function gemm(alpha::Complex128, h::LazyTensor, op::Matrix{Complex128}, beta::Complex128, result::Matrix{Complex128}) - if beta == Complex128(0.) - fill!(result, beta) - elseif beta != Complex128(1.) - scale!(beta, result) - end - N_k = length(h.basis_l.bases) - shape = [min(h.basis_l.shape[i], h.basis_r.shape[i]) for i=1:length(h.basis_l.shape)] - strides_j = operators_dense._strides(h.basis_l.shape) - strides_k = operators_dense._strides(h.basis_r.shape) - _gemm_recursive_lazy_dense(1, N_k, 1, 1, alpha*h.factor, shape, strides_k, strides_j, h.indices, h, op, result) -end - -operators.gemm!(alpha, h::LazyTensor, op::DenseOperator, beta, result::DenseOperator) = gemm(convert(Complex128, alpha), h, op.data, convert(Complex128, beta), result.data) -operators.gemm!(alpha, op::DenseOperator, h::LazyTensor, beta, result::DenseOperator) = gemm(convert(Complex128, alpha), op.data, h, convert(Complex128, beta), result.data) - -function operators.gemv!(alpha::Complex128, a::LazyTensor, b::Ket, beta::Complex128, result::Ket) - b_data = reshape(b.data, length(b.data), 1) - result_data = reshape(result.data, length(result.data), 1) - gemm(alpha, a, b_data, beta, result_data) -end - -function operators.gemv!(alpha::Complex128, a::Bra, b::LazyTensor, beta::Complex128, result::Bra) - a_data = reshape(a.data, 1, length(a.data)) - result_data = reshape(result.data, 1, length(result.data)) - gemm(alpha, a_data, b, beta, result_data) -end - -operators.gemv!(alpha, a::LazyTensor, b::Ket, beta, result::Ket) = operators.gemv!(convert(Complex128, alpha), a, b, convert(Complex128, beta), result) -operators.gemv!(alpha, a::Bra, b::LazyTensor, beta, result::Bra) = operators.gemv!(convert(Complex128, alpha), a, b, convert(Complex128, beta), result) - -end # module diff --git a/src/operators_sparse.jl b/src/operators_sparse.jl deleted file mode 100644 index a165d03a..00000000 --- a/src/operators_sparse.jl +++ /dev/null @@ -1,138 +0,0 @@ -module operators_sparse - -export SparseOperator, diagonaloperator - -import Base: ==, *, /, +, - -import ..operators - -using ..bases, ..states, ..operators, ..operators_dense, ..sparsematrix - - -""" - SparseOperator(b1[, b2, data]) - -Sparse array implementation of Operator. - -The matrix is stored as the julia built-in type `SparseMatrixCSC` -in the `data` field. -""" -mutable struct SparseOperator <: Operator - basis_l::Basis - basis_r::Basis - data::SparseMatrixCSC{Complex128, Int} - function SparseOperator(b1::Basis, b2::Basis, data) - if length(b1) != size(data, 1) || length(b2) != size(data, 2) - throw(DimensionMismatch()) - end - new(b1, b2, data) - end -end - -SparseOperator(b::Basis, data::SparseMatrixCSC{Complex128, Int}) = SparseOperator(b, b, data) -SparseOperator(b::Basis, data::Matrix{Complex128}) = SparseOperator(b, sparse(data)) -SparseOperator(op::DenseOperator) = SparseOperator(op.basis_l, op.basis_r, sparse(op.data)) - -SparseOperator(b1::Basis, b2::Basis) = SparseOperator(b1, b2, spzeros(Complex128, length(b1), length(b2))) -SparseOperator(b::Basis) = SparseOperator(b, b) - -Base.copy(x::SparseOperator) = SparseOperator(x.basis_l, x.basis_r, copy(x.data)) -Base.full(a::SparseOperator) = DenseOperator(a.basis_l, a.basis_r, full(a.data)) - -""" - sparse(op::Operator) - -Convert an arbitrary operator into a [`SparseOperator`](@ref). -""" -Base.sparse(a::Operator) = throw(ArgumentError("Direct conversion from $(typeof(a)) not implemented. Use sparse(full(op)) instead.")) -Base.sparse(a::SparseOperator) = copy(a) -Base.sparse(a::DenseOperator) = SparseOperator(a.basis_l, a.basis_r, sparse(a.data)) - -==(x::SparseOperator, y::SparseOperator) = (x.basis_l == y.basis_l) && (x.basis_r == y.basis_r) && (x.data == y.data) - - -# Arithmetic operations -+(a::SparseOperator, b::SparseOperator) = (check_samebases(a,b); SparseOperator(a.basis_l, a.basis_r, a.data+b.data)) -+(a::SparseOperator, b::DenseOperator) = (check_samebases(a,b); DenseOperator(a.basis_l, a.basis_r, a.data+b.data)) -+(a::DenseOperator, b::SparseOperator) = (check_samebases(a,b); DenseOperator(a.basis_l, a.basis_r, a.data+b.data)) - --(a::SparseOperator) = SparseOperator(a.basis_l, a.basis_r, -a.data) --(a::SparseOperator, b::SparseOperator) = (check_samebases(a,b); SparseOperator(a.basis_l, a.basis_r, a.data-b.data)) --(a::SparseOperator, b::DenseOperator) = (check_samebases(a,b); DenseOperator(a.basis_l, a.basis_r, a.data-b.data)) --(a::DenseOperator, b::SparseOperator) = (check_samebases(a,b); DenseOperator(a.basis_l, a.basis_r, a.data-b.data)) - -*(a::SparseOperator, b::SparseOperator) = (check_multiplicable(a, b); SparseOperator(a.basis_l, b.basis_r, a.data*b.data)) -*(a::SparseOperator, b::Number) = SparseOperator(a.basis_l, a.basis_r, complex(b)*a.data) -*(a::Number, b::SparseOperator) = SparseOperator(b.basis_l, b.basis_r, complex(a)*b.data) - -/(a::SparseOperator, b::Number) = SparseOperator(a.basis_l, a.basis_r, a.data/complex(b)) - -operators.dagger(x::SparseOperator) = SparseOperator(x.basis_r, x.basis_l, x.data') -operators.ishermitian(A::SparseOperator) = ishermitian(A.data) - -operators.tensor(a::SparseOperator, b::SparseOperator) = SparseOperator(tensor(a.basis_l, b.basis_l), tensor(a.basis_r, b.basis_r), kron(b.data, a.data)) -operators.tensor(a::DenseOperator, b::SparseOperator) = SparseOperator(tensor(a.basis_l, b.basis_l), tensor(a.basis_r, b.basis_r), kron(b.data, a.data)) -operators.tensor(a::SparseOperator, b::DenseOperator) = SparseOperator(tensor(a.basis_l, b.basis_l), tensor(a.basis_r, b.basis_r), kron(b.data, a.data)) - -operators.trace(op::SparseOperator) = (check_samebases(op); trace(op.data)) - -operators.conj(op::SparseOperator) = SparseOperator(op.basis_l, op.basis_r, conj(op.data)) -operators.conj!(op::SparseOperator) = conj!(op.data) - -function operators.ptrace(op::SparseOperator, indices::Vector{Int}) - operators.check_ptrace_arguments(op, indices) - shape = [op.basis_l.shape; op.basis_r.shape] - data = sparsematrix.ptrace(op.data, shape, indices) - b_l = ptrace(op.basis_l, indices) - b_r = ptrace(op.basis_r, indices) - SparseOperator(b_l, b_r, data) -end - -function operators.expect(op::SparseOperator, state::Ket)# where T <: Union{Ket, Bra} - check_samebases(op.basis_r, state.basis) - check_samebases(op.basis_l, state.basis) - state.data' * op.data * state.data -end - - -function operators.expect(op::SparseOperator, state::DenseOperator) - check_samebases(op.basis_r, state.basis_l) - check_samebases(op.basis_l, state.basis_r) - result = Complex128(0.) - @inbounds for colindex = 1:op.data.n - for i=op.data.colptr[colindex]:op.data.colptr[colindex+1]-1 - result += op.data.nzval[i]*state.data[colindex, op.data.rowval[i]] - end - end - result -end - -function operators.permutesystems(rho::SparseOperator, perm::Vector{Int}) - @assert length(rho.basis_l.bases) == length(rho.basis_r.bases) == length(perm) - @assert isperm(perm) - shape = [rho.basis_l.shape; rho.basis_r.shape] - data = sparsematrix.permutedims(rho.data, shape, [perm; perm + length(perm)]) - SparseOperator(permutesystems(rho.basis_l, perm), permutesystems(rho.basis_r, perm), data) -end - -operators.identityoperator(::Type{SparseOperator}, b1::Basis, b2::Basis) = SparseOperator(b1, b2, speye(Complex128, length(b1), length(b2))) -operators.identityoperator(b1::Basis, b2::Basis) = identityoperator(SparseOperator, b1, b2) -operators.identityoperator(b::Basis) = identityoperator(b, b) - -""" - diagonaloperator(b::Basis) - -Create a diagonal operator of type [`SparseOperator`](@ref). -""" -function diagonaloperator(b::Basis, diag::Vector{T}) where T <: Number - @assert 1 <= length(diag) <= prod(b.shape) - SparseOperator(b, spdiagm(convert(Vector{Complex128}, diag))) -end - - -# Fast in-place multiplication implementations -operators.gemm!(alpha, M::SparseOperator, b::DenseOperator, beta, result::DenseOperator) = sparsematrix.gemm!(convert(Complex128, alpha), M.data, b.data, convert(Complex128, beta), result.data) -operators.gemm!(alpha, a::DenseOperator, M::SparseOperator, beta, result::DenseOperator) = sparsematrix.gemm!(convert(Complex128, alpha), a.data, M.data, convert(Complex128, beta), result.data) -operators.gemv!(alpha, M::SparseOperator, b::Ket, beta, result::Ket) = sparsematrix.gemv!(convert(Complex128, alpha), M.data, b.data, convert(Complex128, beta), result.data) -operators.gemv!(alpha, b::Bra, M::SparseOperator, beta, result::Bra) = sparsematrix.gemv!(convert(Complex128, alpha), b.data, M.data, convert(Complex128, beta), result.data) - -end # module diff --git a/src/particle.jl b/src/particle.jl deleted file mode 100644 index f9073feb..00000000 --- a/src/particle.jl +++ /dev/null @@ -1,571 +0,0 @@ -module particle - -export PositionBasis, MomentumBasis, - gaussianstate, - spacing, samplepoints, - position, momentum, potentialoperator, - transform - -import Base: ==, position -import ..operators - -using ..bases, ..states, ..operators, ..operators_dense, ..operators_sparse - - -""" - PositionBasis(xmin, xmax, Npoints) - PositionBasis(b::MomentumBasis) - -Basis for a particle in real space. - -For simplicity periodic boundaries are assumed which means that -the rightmost point defined by `xmax` is not included in the basis -but is defined to be the same as `xmin`. - -When a [`MomentumBasis`](@ref) is given as argument the exact values -of ``x_{min}`` and ``x_{max}`` are due to the periodic boundary conditions -more or less arbitrary and are chosen to be -``-\\pi/dp`` and ``\\pi/dp`` with ``dp=(p_{max}-p_{min})/N``. -""" -mutable struct PositionBasis <: Basis - shape::Vector{Int} - xmin::Float64 - xmax::Float64 - N::Int - PositionBasis(xmin::Real, xmax::Real, N::Int) = new([N], xmin, xmax, N) -end - -""" - MomentumBasis(pmin, pmax, Npoints) - MomentumBasis(b::PositionBasis) - -Basis for a particle in momentum space. - -For simplicity periodic boundaries are assumed which means that -`pmax` is not included in the basis but is defined to be the same as `pmin`. - -When a [`PositionBasis`](@ref) is given as argument the exact values -of ``p_{min}`` and ``p_{max}`` are due to the periodic boundary conditions -more or less arbitrary and are chosen to be -``-\\pi/dx`` and ``\\pi/dx`` with ``dx=(x_{max}-x_{min})/N``. -""" -mutable struct MomentumBasis <: Basis - shape::Vector{Int} - pmin::Float64 - pmax::Float64 - N::Int - MomentumBasis(pmin::Real, pmax::Real, N::Int) = new([N], pmin, pmax, N) -end - -PositionBasis(b::MomentumBasis) = (dp = (b.pmax - b.pmin)/b.N; PositionBasis(-pi/dp, pi/dp, b.N)) -MomentumBasis(b::PositionBasis) = (dx = (b.xmax - b.xmin)/b.N; MomentumBasis(-pi/dx, pi/dx, b.N)) - -==(b1::PositionBasis, b2::PositionBasis) = b1.xmin==b2.xmin && b1.xmax==b2.xmax && b1.N==b2.N -==(b1::MomentumBasis, b2::MomentumBasis) = b1.pmin==b2.pmin && b1.pmax==b2.pmax && b1.N==b2.N - - -""" - gaussianstate(b::PositionBasis, x0, p0, sigma) - gaussianstate(b::MomentumBasis, x0, p0, sigma) - -Create a Gaussian state around `x0` and` p0` with width `sigma`. - -In real space the gaussian state is defined as - -```math -\\Psi(x) = \\frac{\\sqrt{\\Delta x}}{\\pi^{1/4}\\sqrt{\\sigma}} - e^{i p_0 (x-\\frac{x_0}{2}) - \\frac{(x-x_0)^2}{2 \\sigma^2}} -``` - -and is connected to the momentum space definition - -```math -\\Psi(p) = \\frac{\\sqrt{\\sigma} \\sqrt{\\Delta x}}{\\pi^{1/4}} - e^{-i x_0 (p-\\frac{p_0}{2}) - \\frac{1}{2}(p-p_0)^2 \\sigma^2} -``` - -via a Fourier-transformation - -```math -\\Psi(p) = \\frac{1}{\\sqrt{2\\pi}} - \\int_{-\\infty}^{\\infty} e^{-ipx}\\Psi(x) \\mathrm{d}x -``` - -The state has the properties - -* ``⟨p⟩ = p_0`` -* ``⟨x⟩ = x_0`` -* ``\\mathrm{Var}(x) = \\frac{σ^2}{2}`` -* ``\\mathrm{Var}(p) = \\frac{1}{2 σ^2}`` - -Due to the numerically necessary discretization additional scaling -factora ``\\sqrt{Δx}`` and ``\\sqrt{Δp}`` are used so that -``Ψx_i = \\sqrt{Δ x} Ψ(x_i)`` and ``Ψp_i = \\sqrt{Δ x} Ψ(p_i)`` so -that the resulting Ket state is normalized. -""" -function gaussianstate(b::PositionBasis, x0::Real, p0::Real, sigma::Real) - psi = Ket(b) - dx = spacing(b) - alpha = 1./(pi^(1/4)*sqrt(sigma))*sqrt(dx) - x = b.xmin - for i=1:b.N - psi.data[i] = alpha*exp(1im*p0*(x-x0/2) - (x-x0)^2/(2*sigma^2)) - x += dx - end - return psi -end - -function gaussianstate(b::MomentumBasis, x0::Real, p0::Real, sigma::Real) - psi = Ket(b) - dp = spacing(b) - alpha = sqrt(sigma)/pi^(1/4)*sqrt(dp) - p = b.pmin - for i=1:b.N - psi.data[i] = alpha*exp(-1im*x0*(p-p0/2) - (p-p0)^2/2*sigma^2) - p += dp - end - return psi -end - - -""" - spacing(b::PositionBasis) - -Difference between two adjacent points of the real space basis. -""" -spacing(b::PositionBasis) = (b.xmax - b.xmin)/b.N -""" - spacing(b::MomentumBasis) - -Momentum difference between two adjacent points of the momentum basis. -""" -spacing(b::MomentumBasis) = (b.pmax - b.pmin)/b.N - -""" - samplepoints(b::PositionBasis) - -x values of the real space basis. -""" -samplepoints(b::PositionBasis) = (dx = spacing(b); Float64[b.xmin + i*dx for i=0:b.N-1]) -""" - samplepoints(b::MomentumBasis) - -p values of the momentum basis. -""" -samplepoints(b::MomentumBasis) = (dp = spacing(b); Float64[b.pmin + i*dp for i=0:b.N-1]) - -""" - position(b::PositionBasis) - -Position operator in real space. -""" -position(b::PositionBasis) = SparseOperator(b, spdiagm(complex(samplepoints(b)), 0, length(b), length(b))) - - -""" - position(b:MomentumBasis) - -Position operator in momentum space. -""" -function position(b::MomentumBasis) - b_pos = PositionBasis(b) - transform(b, b_pos)*full(position(b_pos))*transform(b_pos, b) -end - -""" - momentum(b:MomentumBasis) - -Momentum operator in momentum space. -""" -momentum(b::MomentumBasis) = SparseOperator(b, spdiagm(complex(samplepoints(b)), 0, length(b), length(b))) - -""" - momentum(b::PositionBasis) - -Momentum operator in real space. -""" -function momentum(b::PositionBasis) - b_mom = MomentumBasis(b) - transform(b, b_mom)*full(momentum(b_mom))*transform(b_mom, b) -end - -""" - potentialoperator(b::PositionBasis, V(x)) - -Operator representing a potential ``V(x)`` in real space. -""" -function potentialoperator(b::PositionBasis, V::Function) - x = samplepoints(b) - diagonaloperator(b, V.(x)) -end - -""" - potentialoperator(b::MomentumBasis, V(x)) - -Operator representing a potential ``V(x)`` in momentum space. -""" -function potentialoperator(b::MomentumBasis, V::Function) - b_pos = PositionBasis(b) - transform(b, b_pos)*full(potentialoperator(b_pos, V))*transform(b_pos, b) -end - -""" - potentialoperator(b::CompositeBasis, V(x, y, z, ...)) - -Operator representing a potential ``V`` in more than one dimension. - -# Arguments -* `b`: Composite basis consisting purely either of `PositionBasis` or - `MomentumBasis`. Note, that calling this with a composite basis in - momentum space might consume a large amount of memory. -* `V`: Function describing the potential. ATTENTION: The number of arguments - accepted by `V` must match the spatial dimension. Furthermore, the order - of the arguments has to match that of the order of the tensor product of - bases (e.g. if `b=bx⊗by⊗bz`, then `V(x,y,z)`). -""" -function potentialoperator(b::CompositeBasis, V::Function) - if isa(b.bases[1], PositionBasis) - potentialoperator_position(b, V) - elseif isa(b.bases[1], MomentumBasis) - potentialoperator_momentum(b, V) - else - throw(IncompatibleBases()) - end -end -function potentialoperator_position(b::CompositeBasis, V::Function) - for base=b.bases - @assert isa(base, PositionBasis) - end - - points = [samplepoints(b1) for b1=b.bases] - dims = length.(points) - n = length(b.bases) - data = Array{Complex128}(dims...) - @inbounds for i=1:length(data) - index = ind2sub(data, i) - args = (points[j][index[j]] for j=1:n) - data[i] = V(args...) - end - - diagonaloperator(b, data[:]) -end -function potentialoperator_momentum(b::CompositeBasis, V::Function) - bases_pos = [] - for base=b.bases - @assert isa(base, MomentumBasis) - push!(bases_pos, PositionBasis(base)) - end - b_pos = tensor(bases_pos...) - transform(b, b_pos)*full(potentialoperator_position(b_pos, V))*transform(b_pos, b) -end - -""" - FFTOperator - -Abstract type for all implementations of FFT operators. -""" -abstract type FFTOperator <: Operator end - -const PlanFFT = Base.DFT.FFTW.cFFTWPlan - -""" - FFTOperators - -Operator performing a fast fourier transformation when multiplied with a state -that is a Ket or an Operator. -""" -mutable struct FFTOperators <: FFTOperator - basis_l::Basis - basis_r::Basis - fft_l!::PlanFFT - fft_r!::PlanFFT - fft_l2!::PlanFFT - fft_r2!::PlanFFT - mul_before::Array{Complex128} - mul_after::Array{Complex128} -end - -""" - FFTKets - -Operator that can only perform fast fourier transformations on Kets. -This is much more memory efficient when only working with Kets. -""" -mutable struct FFTKets <: FFTOperator - basis_l::Basis - basis_r::Basis - fft_l!::PlanFFT - fft_r!::PlanFFT - mul_before::Array{Complex128} - mul_after::Array{Complex128} -end - -""" - transform(b1::MomentumBasis, b2::PositionBasis) - transform(b1::PositionBasis, b2::MomentumBasis) - -Transformation operator between position basis and momentum basis. -""" -function transform(basis_l::MomentumBasis, basis_r::PositionBasis; ket_only::Bool=false) - Lx = (basis_r.xmax - basis_r.xmin) - dp = spacing(basis_l) - dx = spacing(basis_r) - if basis_l.N != basis_r.N || abs(2*pi/dp - Lx)/Lx > 1e-12 - throw(IncompatibleBases()) - end - mul_before = exp.(-1im*basis_l.pmin*(samplepoints(basis_r)-basis_r.xmin)) - mul_after = exp.(-1im*basis_r.xmin*samplepoints(basis_l))/sqrt(basis_r.N) - x = Vector{Complex128}(length(basis_r)) - if ket_only - FFTKets(basis_l, basis_r, plan_bfft!(x), plan_fft!(x), mul_before, mul_after) - else - A = Matrix{Complex128}(length(basis_r), length(basis_r)) - FFTOperators(basis_l, basis_r, plan_bfft!(x), plan_fft!(x), plan_bfft!(A, 2), plan_fft!(A, 1), mul_before, mul_after) - end -end - -""" - transform(b1::CompositeBasis, b2::CompositeBasis) - -Transformation operator between two composite bases. Each of the bases -has to contain bases of type PositionBasis and the other one a corresponding -MomentumBasis. -""" -function transform(basis_l::PositionBasis, basis_r::MomentumBasis; ket_only::Bool=false) - Lx = (basis_l.xmax - basis_l.xmin) - dp = spacing(basis_r) - dx = spacing(basis_l) - if basis_l.N != basis_r.N || abs(2*pi/dp - Lx)/Lx > 1e-12 - throw(IncompatibleBases()) - end - mul_before = exp.(1im*basis_l.xmin*(samplepoints(basis_r)-basis_r.pmin)) - mul_after = exp.(1im*basis_r.pmin*samplepoints(basis_l))/sqrt(basis_r.N) - x = Vector{Complex128}(length(basis_r)) - if ket_only - FFTKets(basis_l, basis_r, plan_fft!(x), plan_bfft!(x), mul_before, mul_after) - else - A = Matrix{Complex128}(length(basis_r), length(basis_r)) - FFTOperators(basis_l, basis_r, plan_fft!(x), plan_bfft!(x), plan_fft!(A, 2), plan_bfft!(A, 1), mul_before, mul_after) - end -end - -function transform(basis_l::CompositeBasis, basis_r::CompositeBasis; ket_only::Bool=false, index::Vector{Int}=Int[]) - @assert length(basis_l.bases) == length(basis_r.bases) - if length(index) == 0 - check_pos = typeof.(basis_l.bases) .== PositionBasis - check_mom = typeof.(basis_l.bases) .== MomentumBasis - if any(check_pos) && !any(check_mom) - index = [1:length(basis_l.bases);][check_pos] - elseif any(check_mom) && !any(check_pos) - index = [1:length(basis_l.bases);][check_mom] - else - throw(IncompatibleBases()) - end - end - if all(typeof.(basis_l.bases[index]) .== PositionBasis) - @assert all(typeof.(basis_r.bases[index]) .== MomentumBasis) - transform_xp(basis_l, basis_r, index; ket_only=ket_only) - elseif all(typeof.(basis_l.bases[index]) .== MomentumBasis) - @assert all(typeof.(basis_r.bases[index]) .== PositionBasis) - transform_px(basis_l, basis_r, index; ket_only=ket_only) - else - throw(IncompatibleBases()) - end -end - -function transform_xp(basis_l::CompositeBasis, basis_r::CompositeBasis, index::Vector{Int}; ket_only::Bool=false) - n = length(basis_l.bases) - Lx = [(b.xmax - b.xmin) for b=basis_l.bases[index]] - dp = [spacing(b) for b=basis_r.bases[index]] - dx = [spacing(b) for b=basis_l.bases[index]] - N = [length(b) for b=basis_l.bases] - for i=1:n - if N[i] != length(basis_r.bases[i]) - throw(IncompatibleBases()) - end - end - for i=1:length(index) - if abs(2*pi/dp[i] - Lx[i])/Lx[i] > 1e-12 - throw(IncompatibleBases()) - end - end - - if index[1] == 1 - mul_before = exp.(1im*basis_l.bases[1].xmin*(samplepoints(basis_r.bases[1])-basis_r.bases[1].pmin)) - mul_after = exp.(1im*basis_r.bases[1].pmin*samplepoints(basis_l.bases[1]))/sqrt(basis_r.bases[1].N) - else - mul_before = ones(N[1]) - mul_after = ones(N[1]) - end - for i=2:n - if any(i .== index) - mul_before = kron(exp.(1im*basis_l.bases[i].xmin*(samplepoints(basis_r.bases[i])-basis_r.bases[i].pmin)), mul_before) - mul_after = kron(exp.(1im*basis_r.bases[i].pmin*samplepoints(basis_l.bases[i]))/sqrt(basis_r.bases[i].N), mul_after) - else - mul_before = kron(ones(N[i]), mul_before) - mul_after = kron(ones(N[i]), mul_after) - end - end - mul_before = reshape(mul_before, (N...)) - mul_after = reshape(mul_after, (N...)) - - x = Array{Complex128}(N...) - if ket_only - FFTKets(basis_l, basis_r, plan_fft!(x, index), plan_bfft!(x, index), mul_before, mul_after) - else - A = Array{Complex128}([N, N;]...) - FFTOperators(basis_l, basis_r, plan_fft!(x, index), plan_bfft!(x, index), plan_fft!(A, [n + 1:2n;][index]), plan_bfft!(A, [1:n;][index]), mul_before, mul_after) - end -end - -function transform_px(basis_l::CompositeBasis, basis_r::CompositeBasis, index::Vector{Int}; ket_only::Bool=false) - n = length(basis_l.bases) - Lx = [(b.xmax - b.xmin) for b=basis_r.bases[index]] - dp = [spacing(b) for b=basis_l.bases[index]] - dx = [spacing(b) for b=basis_r.bases[index]] - N = [length(b) for b=basis_l.bases] - for i=1:n - if N[i] != length(basis_r.bases[i]) - throw(IncompatibleBases()) - end - end - for i=1:length(index) - if abs(2*pi/dp[i] - Lx[i])/Lx[i] > 1e-12 - throw(IncompatibleBases()) - end - end - - if index[1] == 1 - mul_before = exp.(-1im*basis_l.bases[1].pmin*(samplepoints(basis_r.bases[1])-basis_r.bases[1].xmin)) - mul_after = exp.(-1im*basis_r.bases[1].xmin*samplepoints(basis_l.bases[1]))/sqrt(N[1]) - else - mul_before = ones(N[1]) - mul_after = ones(N[1]) - end - for i=2:n - if i in index - mul_before = kron(exp.(-1im*basis_l.bases[i].pmin*(samplepoints(basis_r.bases[i])-basis_r.bases[i].xmin)), mul_before) - mul_after = kron(exp.(-1im*basis_r.bases[i].xmin*samplepoints(basis_l.bases[i]))/sqrt(N[i]), mul_after) - else - mul_before = kron(ones(N[i]), mul_before) - mul_after = kron(ones(N[i]), mul_after) - end - end - mul_before = reshape(mul_before, (N...)) - mul_after = reshape(mul_after, (N...)) - - x = Array{Complex128}(N...) - if ket_only - FFTKets(basis_l, basis_r, plan_bfft!(x, index), plan_fft!(x, index), mul_before, mul_after) - else - A = Array{Complex128}([N, N;]...) - FFTOperators(basis_l, basis_r, plan_bfft!(x, index), plan_fft!(x, index), plan_bfft!(A, [n + 1:2n;][index]), plan_fft!(A, [1:n;][index]), mul_before, mul_after) - end -end - -operators.full(op::FFTOperators) = op*identityoperator(DenseOperator, op.basis_r) - -operators.dagger(op::FFTOperators) = transform(op.basis_r, op.basis_l) -operators.dagger(op::FFTKets) = transform(op.basis_r, op.basis_l; ket_only=true) - -operators.tensor(A::FFTOperators, B::FFTOperators) = transform(tensor(A.basis_l, B.basis_l), tensor(A.basis_r, B.basis_r)) -operators.tensor(A::FFTKets, B::FFTKets) = transform(tensor(A.basis_l, B.basis_l), tensor(A.basis_r, B.basis_r); ket_only=true) - -function operators.gemv!(alpha_, M::FFTOperator, b::Ket, beta_, result::Ket) - alpha = convert(Complex128, alpha_) - beta = convert(Complex128, beta_) - N::Int = length(M.basis_r) - if beta==Complex(0.) - @inbounds for i=1:N - result.data[i] = M.mul_before[i] * b.data[i] - end - M.fft_r! * reshape(result.data, size(M.mul_before)) - @inbounds for i=1:N - result.data[i] *= M.mul_after[i] * alpha - end - else - psi_ = Ket(M.basis_l, copy(b.data)) - @inbounds for i=1:N - psi_.data[i] *= M.mul_before[i] - end - M.fft_r! * reshape(psi_.data, size(M.mul_before)) - @inbounds for i=1:N - result.data[i] = beta*result.data[i] + alpha * psi_.data[i] * M.mul_after[i] - end - end - nothing -end - -function operators.gemv!(alpha_, b::Bra, M::FFTOperator, beta_, result::Bra) - alpha = convert(Complex128, alpha_) - beta = convert(Complex128, beta_) - N::Int = length(M.basis_l) - if beta==Complex(0.) - @inbounds for i=1:N - result.data[i] = conj(M.mul_after[i]) * conj(b.data[i]) - end - M.fft_l! * reshape(result.data, size(M.mul_after)) - @inbounds for i=1:N - result.data[i] = conj(result.data[i]) * M.mul_before[i] * alpha - end - else - psi_ = Bra(M.basis_r, conj(b.data)) - @inbounds for i=1:N - psi_.data[i] *= conj(M.mul_after[i]) - end - M.fft_l! * reshape(psi_.data, size(M.mul_after)) - @inbounds for i=1:N - result.data[i] = beta*result.data[i] + alpha * conj(psi_.data[i]) * M.mul_before[i] - end - end - nothing -end - -function operators.gemm!(alpha_, A::DenseOperator, B::FFTOperators, beta_, result::DenseOperator) - alpha = convert(Complex128, alpha_) - beta = convert(Complex128, beta_) - if beta != Complex(0.) - data = Matrix{Complex128}(size(result.data, 1), size(result.data, 2)) - else - data = result.data - end - copy!(data, A.data) - scale!(data, B.mul_after[:]) - conj!(data) - B.fft_l2! * reshape(data, [size(B.mul_after)..., size(B.mul_after)...;]...) - conj!(data) - scale!(data, B.mul_before[:]) - if alpha != Complex(1.) - scale!(alpha, data) - end - if beta != Complex(0.) - scale!(result.data, beta) - result.data += data - end - nothing -end - -function operators.gemm!(alpha_, A::FFTOperators, B::DenseOperator, beta_, result::DenseOperator) - alpha = convert(Complex128, alpha_) - beta = convert(Complex128, beta_) - if beta != Complex(0.) - data = Matrix{Complex128}(size(result.data, 1), size(result.data, 2)) - else - data = result.data - end - copy!(data, B.data) - scale!(A.mul_before[:], data) - A.fft_r2! * reshape(data, [size(A.mul_before)..., size(A.mul_before)...;]...) - scale!(A.mul_after[:], data) - if alpha != Complex(1.) - scale!(alpha, data) - end - if beta != Complex(0.) - scale!(result.data, beta) - result.data += data - end - nothing -end - - -end # module diff --git a/src/phasespace.jl b/src/phasespace.jl old mode 100755 new mode 100644 index 5fb87c2a..d0afd3d0 --- a/src/phasespace.jl +++ b/src/phasespace.jl @@ -1,9 +1,3 @@ -module phasespace - -export qfunc, wigner, coherentspinstate, qfuncsu2, wignersu2, ylm - -using ..bases, ..states, ..operators, ..operators_dense, ..fock, ..spin - import WignerSymbols: clebschgordan """ @@ -16,30 +10,27 @@ function can either be evaluated on one point α or on a grid specified by the vectors `xvec` and `yvec`. Note that conversion from `x` and `y` to `α` is done via the relation ``α = \\frac{1}{\\sqrt{2}}(x + i y)``. """ -function qfunc(rho::Operator, alpha::Number) +function qfunc(rho::AbstractOperator{B,B}, alpha::Number) where B<:FockBasis b = basis(rho) - @assert isa(b, FockBasis) - _qfunc_operator(rho, convert(Complex128, alpha), Ket(b), Ket(b)) + _qfunc_operator(rho, convert(ComplexF64, alpha), Ket(b), Ket(b)) end -function qfunc(rho::Operator, xvec::Vector{Float64}, yvec::Vector{Float64}) +function qfunc(rho::AbstractOperator{B,B}, xvec::Vector{Float64}, yvec::Vector{Float64}) where B<:FockBasis b = basis(rho) - @assert isa(b, FockBasis) Nx = length(xvec) Ny = length(yvec) tmp1 = Ket(b) tmp2 = Ket(b) - result = Matrix{Complex128}(Nx, Ny) + result = Matrix{ComplexF64}(undef, Nx, Ny) @inbounds for j=1:Ny, i=1:Nx result[i, j] = _qfunc_operator(rho, complex(xvec[i], yvec[j])/sqrt(2), tmp1, tmp2) end result end -function qfunc(psi::Ket, alpha::Number) +function qfunc(psi::Ket{B}, alpha::Number) where B<:FockBasis b = basis(psi) - @assert isa(b, FockBasis) - _alpha = convert(Complex128, alpha) + _alpha = convert(ComplexF64, alpha) _conj_alpha = conj(_alpha) N = length(psi.basis) s = psi.data[N]/sqrt(N-1) @@ -50,9 +41,8 @@ function qfunc(psi::Ket, alpha::Number) return abs2(s)*exp(-abs2(_alpha))/pi end -function qfunc(psi::Ket, xvec::Vector{Float64}, yvec::Vector{Float64}) +function qfunc(psi::Ket{B}, xvec::Vector{Float64}, yvec::Vector{Float64}) where B<:FockBasis b = basis(psi) - @assert isa(b, FockBasis) Nx = length(xvec) Ny = length(yvec) points = Nx*Ny @@ -74,13 +64,13 @@ function qfunc(psi::Ket, xvec::Vector{Float64}, yvec::Vector{Float64}) result end -function qfunc(state::Union{Ket, Operator}, x::Number, y::Number) - qfunc(state, Complex128(x, y)/sqrt(2)) +function qfunc(state::Union{Ket{B}, AbstractOperator{B,B}}, x::Number, y::Number) where B<:FockBasis + qfunc(state, ComplexF64(x, y)/sqrt(2)) end -function _qfunc_operator(rho::Operator, alpha::Complex128, tmp1::Ket, tmp2::Ket) - coherentstate(basis(rho), alpha, tmp1) - operators.gemv!(complex(1.), rho, tmp1, complex(0.), tmp2) +function _qfunc_operator(rho::AbstractOperator{B,B}, alpha::ComplexF64, tmp1::Ket, tmp2::Ket) where B<:FockBasis + coherentstate!(tmp1, basis(rho), alpha) + QuantumOpticsBase.gemv!(complex(1.), rho, tmp1, complex(0.), tmp2) a = dot(tmp1.data, tmp2.data) return a/pi end @@ -96,9 +86,8 @@ function can either be evaluated on one point α or on a grid specified by the vectors `xvec` and `yvec`. Note that conversion from `x` and `y` to `α` is done via the relation ``α = \\frac{1}{\\sqrt{2}}(x + i y)``. """ -function wigner(rho::DenseOperator, x::Number, y::Number) +function wigner(rho::DenseOperator{B,B}, x::Number, y::Number) where B<:FockBasis b = basis(rho) - @assert isa(b, FockBasis) N = b.N::Int _2α = complex(convert(Float64, x), convert(Float64, y))*sqrt(2) abs2_2α = abs2(_2α) @@ -113,13 +102,12 @@ function wigner(rho::DenseOperator, x::Number, y::Number) exp(-abs2_2α/2)/pi*real(w) end -function wigner(rho::DenseOperator, xvec::Vector{Float64}, yvec::Vector{Float64}) +function wigner(rho::DenseOperator{B,B}, xvec::Vector{Float64}, yvec::Vector{Float64}) where B<:FockBasis b = basis(rho) - @assert isa(b, FockBasis) N = b.N::Int _2α = [complex(x, y)*sqrt(2) for x=xvec, y=yvec] abs2_2α = abs2.(_2α) - w = zeros(_2α) + w = zero(_2α) b0 = similar(_2α) b1 = similar(_2α) b2 = similar(_2α) @@ -137,9 +125,9 @@ wigner(psi::Ket, x, y) = wigner(dm(psi), x, y) wigner(state, alpha::Number) = wigner(state, real(alpha)*sqrt(2), imag(alpha)*sqrt(2)) -function _clenshaw_grid(L::Int, ρ::Matrix{Complex128}, - abs2_2α::Matrix{Float64}, _2α::Matrix{Complex128}, w::Matrix{Complex128}, - b0::Matrix{Complex128}, b1::Matrix{Complex128}, b2::Matrix{Complex128}, scale::Int) +function _clenshaw_grid(L::Int, ρ::Matrix{ComplexF64}, + abs2_2α::Matrix{Float64}, _2α::Matrix{ComplexF64}, w::Matrix{ComplexF64}, + b0::Matrix{ComplexF64}, b1::Matrix{ComplexF64}, b2::Matrix{ComplexF64}, scale::Int) n = size(ρ, 1)-L-1 points = length(w) if n==0 @@ -179,7 +167,7 @@ function _clenshaw_grid(L::Int, ρ::Matrix{Complex128}, end end -function _clenshaw(L::Int, abs2_2α::Float64, ρ::Matrix{Complex128}) +function _clenshaw(L::Int, abs2_2α::Float64, ρ::Matrix{ComplexF64}) n = size(ρ, 1)-L-1 if n==0 return ρ[1, L+1] @@ -215,7 +203,7 @@ parametrization is not unique), similarly to a qubit on the Bloch sphere. """ function coherentspinstate(b::SpinBasis, theta::Real, phi::Real, - result = Ket(b, Vector{Complex128}(length(b)))) + result = Ket(b, Vector{ComplexF64}(undef, length(b)))) data = result.data N = BigInt(length(b)-1) sinth = sin(0.5theta) @@ -243,23 +231,21 @@ resolution `(Ntheta, Nphi)`. This version calculates the Husimi Q SU(2) function at a position given by θ and ϕ. """ -function qfuncsu2(psi::Ket, Ntheta::Int; Nphi::Int=2Ntheta) +function qfuncsu2(psi::Ket{B}, Ntheta::Int; Nphi::Int=2Ntheta) where B<:SpinBasis b = psi.basis psi_bra_data = psi.data' lb = float(b.spinnumber) - @assert isa(b, SpinBasis) - result = Array{Float64}(Ntheta,Nphi) + result = Array{Float64}(undef, Ntheta,Nphi) @inbounds for i = 0:Ntheta-1, j = 0:Nphi-1 result[i+1,j+1] = (2*lb+1)/(4pi)*abs2(psi_bra_data*coherentspinstate(b,pi-i*pi/(Ntheta-1),j*2pi/(Nphi-1)-pi).data) end return result end -function qfuncsu2(rho::DenseOperator, Ntheta::Int; Nphi::Int=2Ntheta) +function qfuncsu2(rho::DenseOperator{B,B}, Ntheta::Int; Nphi::Int=2Ntheta) where B<:SpinBasis b = basis(rho) lb = float(b.spinnumber) - @assert isa(b, SpinBasis) - result = Array{Float64}(Ntheta,Nphi) + result = Array{Float64}(undef, Ntheta,Nphi) @inbounds for i = 0:Ntheta-1, j = 0:Nphi-1 c = coherentspinstate(b,pi-i*1pi/(Ntheta-1),j*2pi/(Nphi-1)-pi) result[i+1,j+1] = abs((2*lb+1)/(4pi)*c.data'*rho.data*c.data) @@ -267,19 +253,17 @@ function qfuncsu2(rho::DenseOperator, Ntheta::Int; Nphi::Int=2Ntheta) return result end -function qfuncsu2(psi::Ket, theta::Real, phi::Real) +function qfuncsu2(psi::Ket{B}, theta::Real, phi::Real) where B<:SpinBasis b = basis(psi) psi_bra_data = psi.data' lb = float(b.spinnumber) - @assert isa(b, SpinBasis) result = (2*lb+1)/(4pi)*abs2(psi_bra_data*coherentspinstate(b,theta,phi).data) return result end -function qfuncsu2(rho::DenseOperator, theta::Real, phi::Real) +function qfuncsu2(rho::DenseOperator{B,B}, theta::Real, phi::Real) where B<:SpinBasis b = basis(rho) lb = float(b.spinnumber) - @assert isa(b, SpinBasis) c = coherentspinstate(b,theta,phi) result = abs((2*lb+1)/(4pi)*c.data'*rho.data*c.data) return result @@ -300,14 +284,14 @@ decomposing the state into the basis of spherical harmonics. This version calculates the Wigner SU(2) function at a position given by θ and ϕ """ -function wignersu2(rho::DenseOperator, theta::Real, phi::Real) +function wignersu2(rho::DenseOperator{B,B}, theta::Real, phi::Real) where B<:SpinBasis N = length(basis(rho))-1 ### Tensor generation ### - BandT = Array{Vector{Float64}}(N,N+1) - BandT[1,1] = collect(linspace(-N/2, N/2, N+1)) - BandT[1,2] = -collect(sqrt.(linspace(1, N, N)).*sqrt.(linspace((N)/2, 1/2, N))) + BandT = Array{Vector{Float64}}(undef, N,N+1) + BandT[1,1] = collect(range(-N/2, stop=N/2, length=N+1)) + BandT[1,2] = -collect(sqrt.(range(1, stop=N, length=N)).*sqrt.(range((N)/2, stop=1/2, length=N))) BandT[2,1] = clebschgordan(1,0,1,0,2,0)*BandT[1,1].*BandT[1,1] - clebschgordan(1,-1,1,1,2,0)*[zeros(N+1-length(BandT[1,2])); BandT[1,2].*BandT[1,2]] - clebschgordan(1,1,1,-1,2,0)*[BandT[1,2].*BandT[1,2]; zeros(N+1-length(BandT[1,2]))] @@ -341,7 +325,7 @@ function wignersu2(rho::DenseOperator, theta::Real, phi::Real) ### State decomposition ### c = rho.data - EVT = Array{Complex128}(N,N+1) + EVT = Array{ComplexF64}(undef, N,N+1) @inbounds for S = 1:N, M = 0:S EVT[S,M+1] = conj(sum(BandT[S,M+1].*diag(c,M))) end @@ -350,14 +334,14 @@ function wignersu2(rho::DenseOperator, theta::Real, phi::Real) return wignermap*sqrt((N+1)/(4pi)) end -function wignersu2(rho::DenseOperator, Ntheta::Int; Nphi::Int=2Ntheta) +function wignersu2(rho::DenseOperator{B,B}, Ntheta::Int; Nphi::Int=2Ntheta) where B<:SpinBasis N = length(basis(rho))-1 ### Tensor generation ### - BandT = Array{Vector{Float64}}(N,N+1) - BandT[1,1] = collect(linspace(-N/2, N/2, N+1)) - BandT[1,2] = -collect(sqrt.(linspace(1, N, N)).*sqrt.(linspace((N)/2, 1/2, N))) + BandT = Array{Vector{Float64}}(undef, N,N+1) + BandT[1,1] = collect(range(-N/2, stop=N/2, length=N+1)) + BandT[1,2] = -collect(sqrt.(range(1, stop=N, length=N)).*sqrt.(range((N)/2, stop=1/2, length=N))) BandT[2,1] = clebschgordan(1,0,1,0,2,0)*BandT[1,1].*BandT[1,1] - clebschgordan(1,-1,1,1,2,0)*[zeros(N+1-length(BandT[1,2])); BandT[1,2].*BandT[1,2]] - clebschgordan(1,1,1,-1,2,0)*[BandT[1,2].*BandT[1,2]; zeros(N+1-length(BandT[1,2]))] @@ -389,19 +373,19 @@ function wignersu2(rho::DenseOperator, Ntheta::Int; Nphi::Int=2Ntheta) ### State decomposition ### c = rho.data - EVT = Array{Complex128}(N,N+1) + EVT = Array{ComplexF64}(undef, N,N+1) @inbounds for S = 1:N, M = 0:S EVT[S,M+1] = conj(sum(BandT[S,M+1].*diag(c,M))) end - wignermap = Array{Float64}(Ntheta,Nphi) + wignermap = Array{Float64}(undef, Ntheta,Nphi) @inbounds for i = 1:Ntheta, j = 1:Nphi wignermap[i,j] = _wignersu2int(N,i*1pi/(Ntheta-1)-1pi/(Ntheta-1),j*2pi/(Nphi-1)-2pi/(Nphi-1)-pi, EVT) end return wignermap*sqrt((N+1)/(4pi)) end -function _wignersu2int(N::Integer, theta::Real, phi::Real, EVT::Array{Complex128, 2}) +function _wignersu2int(N::Integer, theta::Real, phi::Real, EVT::Array{ComplexF64, 2}) UberBand = sqrt(1/(1+N))*ylm(0,0,theta,phi) @inbounds for S = 1:N @inbounds for M = 1:S @@ -424,7 +408,7 @@ This function calculates the value of Y(l,m) spherical harmonic at position θ a function ylm(l::Integer,m::Integer,theta::Real,phi::Real) phi_ = mod(phi,2pi) theta_ = mod(theta,2pi) - phase = e^(1.0im*m*phi_) + phase = exp(1.0im*m*phi_) if theta_ ≈ 0 if m == 0 return @. phase*sqrt((2*l+1)/pi)/2 @@ -479,5 +463,3 @@ function _calc_ylm_norm(l::Int, m_::Int) end norm end - -end #module diff --git a/src/polynomials.jl b/src/polynomials.jl deleted file mode 100644 index e89c435f..00000000 --- a/src/polynomials.jl +++ /dev/null @@ -1,107 +0,0 @@ -module polynomials - -export horner, hermite, laguerre - -""" - horner(coefficients, x) - -Evaluate the given polynomial at position x using the Horner scheme. - -```math -p(x) = \\sum_{n=0}^N c_n x^n -``` -""" -function horner(coefficients::Vector{T}, x::Number) where T<:Number - bn = coefficients[end] - for n=length(coefficients)-1:-1:1 - bn = coefficients[n] + bn*x - end - bn -end - - -module hermite - -""" - a_nk(N) - -Calculate the all coefficients for all Hermite polynomials up to order N. - -```math -H_n(x) = \\sum_{k=0}^n a_{n,k} x^k -``` - -Returns a vector of length N+1 where the n-th entry contains all coefficients -for the n-th Hermite polynomial. -""" -function a(N::Int) - a = Vector{Vector{Int}}(N+1) - a[1] = [1] - a[2] = [0,2] - am = a[2] - for n=2:N - an = zeros(Int, n+1) - a[n+1] = an - if iseven(n) - an[1] = -am[2] - end - an[n+1] = 2*am[n] - if iseven(n) - for k=3:2:n-1 - an[k] = 2*am[k-1] - am[k+1]*k - end - else - for k=2:2:n-1 - an[k] = 2*am[k-1] - am[k+1]*k - end - end - am = an - end - a -end - -""" - A_nk(N) - -Calculate the all scaled coefficients for all Hermite polynomials up to order N. - -The scaled coefficients `A` are connected to the unscaled coefficients `a` by -the relation ``A_{n,k} = \\frac{a_{n,k}}{\\sqrt{2^n n!}}`` - -Returns a vector of length N+1 where the n-th entry contains all scaled -coefficients for the n-th Hermite polynomial. -""" -function A(N) - A = Vector{Vector{Float64}}(N+1) - A[1] = [1.] - A[2] = [0., sqrt(2)] - Am = A[2] - for n=2:N - An = zeros(Float64, n+1) - A[n+1] = An - if iseven(n) - An[1] = -Am[2]/sqrt(2*n) - end - An[n+1] = Am[n]*sqrt(2/n) - if iseven(n) - for k=3:2:n-1 - An[k] = Am[k-1]*sqrt(2/n) - Am[k+1]*k/sqrt(2*n) - end - else - for k=2:2:n-1 - An[k] = Am[k-1]*sqrt(2/n) - Am[k+1]*k/sqrt(2*n) - end - end - Am = An - end - A -end - -end # module hermite - - -module laguerre - -end # module laguerre - -end # module polynomials \ No newline at end of file diff --git a/src/printing.jl b/src/printing.jl deleted file mode 100644 index e3298d55..00000000 --- a/src/printing.jl +++ /dev/null @@ -1,485 +0,0 @@ -module printing - -export set_printing - -import Base: show - -using Compat -using ..bases, ..states -using ..operators, ..operators_dense, ..operators_sparse -using ..operators_lazytensor, ..operators_lazysum, ..operators_lazyproduct -using ..spin, ..fock, ..nlevel, ..particle, ..subspace, ..manybody, ..sparsematrix - - -""" - QuantumOptics.set_printing(; standard_order, rounding_tol) - -Set options for REPL output. - -# Arguments -* `standard_order=false`: For performance reasons, the order of the tensor - product is inverted, i.e. `tensor(a, b)=kron(b, a)`. When changing this - to `true`, the output shown in the REPL will exhibit the correct order. -* `rounding_tol=1e-17`: Tolerance for floating point errors shown in the output. -""" -function set_printing(; standard_order::Bool=_std_order, rounding_tol::Real=_round_tol) - global _std_order = standard_order - global _round_tol = rounding_tol - global machineprecorder = Int32(round(-log10(_round_tol), 0)) - nothing -end -set_printing(standard_order=false, rounding_tol=1e-17) - -function show(stream::IO, x::GenericBasis) - if length(x.shape) == 1 - write(stream, "Basis(dim=$(x.shape[1]))") - else - s = replace(string(x.shape), " ", "") - write(stream, "Basis(shape=$s)") - end -end - -function show(stream::IO, x::CompositeBasis) - write(stream, "[") - for i in 1:length(x.bases) - show(stream, x.bases[i]) - if i != length(x.bases) - write(stream, " ⊗ ") - end - end - write(stream, "]") -end - -function show(stream::IO, x::SpinBasis) - d = denominator(x.spinnumber) - n = numerator(x.spinnumber) - if d == 1 - write(stream, "Spin($n)") - else - write(stream, "Spin($n/$d)") - end -end - -function show(stream::IO, x::FockBasis) - write(stream, "Fock(cutoff=$(x.N))") -end - -function show(stream::IO, x::NLevelBasis) - write(stream, "NLevel(N=$(x.N))") -end - -function show(stream::IO, x::PositionBasis) - write(stream, "Position(xmin=$(x.xmin), xmax=$(x.xmax), N=$(x.N))") -end - -function show(stream::IO, x::MomentumBasis) - write(stream, "Momentum(pmin=$(x.pmin), pmax=$(x.pmax), N=$(x.N))") -end - -function show(stream::IO, x::SubspaceBasis) - write(stream, "Subspace(superbasis=$(x.superbasis), states:$(length(x.basisstates)))") -end - -function show(stream::IO, x::ManyBodyBasis) - write(stream, "ManyBody(onebodybasis=$(x.onebodybasis), states:$(length(x.occupations)))") -end - -function show(stream::IO, x::Ket) - write(stream, "Ket(dim=$(length(x.basis)))\n basis: $(x.basis)\n") - if !_std_order - Base.showarray(stream, x.data, false; header=false) - else - showarray_stdord(stream, x.data, x.basis.shape, false, header=false) - end -end - -function show(stream::IO, x::Bra) - write(stream, "Bra(dim=$(length(x.basis)))\n basis: $(x.basis)\n") - if !_std_order - Base.showarray(stream, x.data, false; header=false) - else - showarray_stdord(stream, x.data, x.basis.shape, false, header=false) - end -end - -function showoperatorheader(stream::IO, x::Operator) - write(stream, "$(typeof(x).name.name)(dim=$(length(x.basis_l))x$(length(x.basis_r)))\n") - if bases.samebases(x) - write(stream, " basis: ") - show(stream, basis(x)) - else - write(stream, " basis left: ") - show(stream, x.basis_l) - write(stream, "\n basis right: ") - show(stream, x.basis_r) - end -end - -show(stream::IO, x::Operator) = showoperatorheader(stream, x) - -function show(stream::IO, x::DenseOperator) - showoperatorheader(stream, x) - write(stream, "\n") - if !_std_order - Base.showarray(stream, x.data, false; header=false) - else - showarray_stdord(stream, x.data, x.basis_l.shape, x.basis_r.shape, false, header=false) - end -end - -function show(stream::IO, x::SparseOperator) - showoperatorheader(stream, x) - if nnz(x.data) == 0 - write(stream, "\n []") - else - if !_std_order - show(stream, x.data) - else - showsparsearray_stdord(stream, x.data, x.basis_l.shape, x.basis_r.shape) - end - end -end - -function show(stream::IO, x::LazyTensor) - showoperatorheader(stream, x) - write(stream, "\n operators: $(length(x.operators))") - s = replace(string(x.indices), " ", "") - write(stream, "\n indices: $s") -end - -function show(stream::IO, x::Union{LazySum, LazyProduct}) - showoperatorheader(stream, x) - write(stream, "\n operators: $(length(x.operators))") -end - -""" - ind2Nary(m::Int, dims::Vector{Int}) - -The inverse of `Nary2ind`. - -# Example -``` -julia> dims = [2,2,3]; - -julia> for i in 1:prod(dims) - println(i,": ", ind2Nary(i,dims)) - end -1: [0, 0, 0] -2: [0, 0, 1] -3: [0, 0, 2] -4: [0, 1, 0] -5: [0, 1, 1] -6: [0, 1, 2] -7: [1, 0, 0] -8: [1, 0, 1] -9: [1, 0, 2] -10: [1, 1, 0] -11: [1, 1, 1] -12: [1, 1, 2] -``` -""" -function ind2Nary(m::Int, dims::Vector{Int}) - m = m - 1 - nq = length(dims) - ar = zeros(Int, nq) - product = prod(dims[2:end]) - for ith in 1:nq-1 - d = div(m, product) - m = m - d * product - product = div(product, dims[ith+1]) - ar[ith] = d - end - ar[end] = m - return ar -end - -""" - Nary2ind(x, dims) -> index - -Convert composite N-arys to index. - -# Example -``` -julia> dims = [2,2,3]; - -julia> Nary2ind([1,1,0], dims) -10 - -julia> for i in 1:prod(dims) - println(i,": ", Nary2ind(ind2Nary(i,dims),dims), ": ", ind2Nary(i,dims)) - end -1: 1: [0, 0, 0] -2: 2: [0, 0, 1] -3: 3: [0, 0, 2] -4: 4: [0, 1, 0] -5: 5: [0, 1, 1] -6: 6: [0, 1, 2] -7: 7: [1, 0, 0] -8: 8: [1, 0, 1] -9: 9: [1, 0, 2] -10: 10: [1, 1, 0] -11: 11: [1, 1, 1] -12: 12: [1, 1, 2] -``` -""" -function Nary2ind(x::Vector{Int}, dims::Vector{Int}) - tmp = 0 - @assert length(x) == length(dims) - nterms = length(x) - tp = prod(dims[2:end]) - for i in 1:nterms-1 - tmp += x[i] * tp - tp = div(tp, dims[i+1]) - end - tmp += x[end] + 1 -end - -""" - mirror_world_index(idx, dims) - -Convert index of standard order to that of inversed order. -This function is named after the book, '鏡の中の物理学' (Physics in the mirror), -written by Tomonaga Shin'ichirō (Japanese physicist). -""" -function mirror_world_index(idx::Int, dims::Vector{Int}) - return Nary2ind( reverse(ind2Nary(idx, dims)), reverse(dims) ) -end - -# Following program is modified: -# julia/base/show.jl -# https://github.com/JuliaLang/julia/blob/5cd144ffa328fdd4cd9e983b616c7205f9bb4f51/base/show.jl -# and -# julia/base/sparse/sparsematrix.jl -# https://github.com/JuliaLang/julia/blob/78831902cc412e298c5fcc94dae1e4382c8e7cd0/base/sparse/sparsematrix.jl - -# Copyright (c) 2009-2018: Jeff Bezanson, Stefan Karpinski, Viral B. Shah, -# and other contributors: -# -# https://github.com/JuliaLang/julia/contributors -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -""" -`alignment_std(X, ldims, rdims, rows, cols, cols_if_complete, cols_otherwise, sep)` returns the -alignment for specified parts of array `X`, returning the (left,right) info. -It will look in X's `rows`, `cols` (both lists of indices) -and figure out what's needed to be fully aligned, for example looking all -the way down a column and finding out the maximum size of each element. -Parameter `sep::Integer` is number of spaces to put between elements. -`cols_if_complete` and `cols_otherwise` indicate screen width to use. -Alignment is reported as a vector of (left,right) tuples, one for each -column going across the screen. -""" -function alignment_std(io::IO, X::AbstractVecOrMat, ldims::Vector, rdims::Vector, - rows::AbstractVector, cols::AbstractVector, - cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer) - a = Tuple{Int, Int}[] - for j in cols # need to go down each column one at a time - l = r = 0 - for i in rows # plumb down and see what largest element sizes are - aij = Base.alignment(io, X[mirror_world_index(i, ldims), mirror_world_index(j, rdims)]) - l = max(l, aij[1]) # left characters - r = max(r, aij[2]) # right characters - end - push!(a, (l, r)) # one tuple per column of X, pruned to screen width - if length(a) > 1 && sum(map(sum,a)) + sep*length(a) >= cols_if_complete - pop!(a) # remove this latest tuple if we're already beyond screen width - break - end - end - if 1 < length(a) < length(indices(X,2)) - while sum(map(sum,a)) + sep*length(a) >= cols_otherwise - pop!(a) - end - end - return a -end - -""" -`print_matrix_row_std(io, X, A, ldims, rdims, i, cols, sep)` produces the aligned output for -a single matrix row X[i, cols] where the desired list of columns is given. -The corresponding alignment A is used, and the separation between elements -is specified as string sep. -`print_matrix_row_std` will also respect compact output for elements. -""" -function print_matrix_row_std(io::IO, - X::AbstractVecOrMat, A::Vector, ldims::Vector, rdims::Vector, - i::Integer, cols::AbstractVector, sep::AbstractString) - isempty(A) || first(indices(cols,1)) == 1 || throw(DimensionMismatch("indices of cols ($(indices(cols,1))) must start at 1")) - for k = 1:length(A) - j = cols[k] - x = X[mirror_world_index(i, ldims), mirror_world_index(j, rdims)] - a = Base.alignment(io, x) - sx = sprint(0, show, x, env=io) - l = repeat(" ", A[k][1]-a[1]) # pad on left and right as needed - r = repeat(" ", A[k][2]-a[2]) - prettysx = Base.replace_in_print_matrix(X,mirror_world_index(i, ldims),mirror_world_index(j, rdims),sx) - print(io, l, prettysx, r) - if k < length(A); print(io, sep); end - end -end - - -function print_matrix_std(io::IO, X::AbstractVecOrMat, ldims::Vector, rdims::Vector, - pre::AbstractString = " ", # pre-matrix string - sep::AbstractString = " ", # separator between elements - post::AbstractString = "", # post-matrix string - hdots::AbstractString = " \u2026 ", - vdots::AbstractString = "\u22ee", - ddots::AbstractString = " \u22f1 ", - hmod::Integer = 5, vmod::Integer = 5) - if !get(io, :limit, false) - screenheight = screenwidth = typemax(Int) - else - sz = displaysize(io) - screenheight, screenwidth = sz[1] - 4, sz[2] - end - screenwidth -= length(pre) + length(post) - presp = repeat(" ", length(pre)) # indent each row to match pre string - postsp = "" - @assert strwidth(hdots) == strwidth(ddots) - sepsize = length(sep) - rowsA, colsA = indices(X,1), indices(X,2) - m, n = length(rowsA), length(colsA) - # To figure out alignments, only need to look at as many rows as could - # fit down screen. If screen has at least as many rows as A, look at A. - # If not, then we only need to look at the first and last chunks of A, - # each half a screen height in size. - halfheight = div(screenheight,2) - if m > screenheight - rowsA = [rowsA[1:halfheight]; rowsA[m-div(screenheight-1,2)+1:m]] - end - # Similarly for columns, only necessary to get alignments for as many - # columns as could conceivably fit across the screen - maxpossiblecols = div(screenwidth, 1+sepsize) - if n > maxpossiblecols - colsA = [colsA[1:maxpossiblecols]; colsA[(n-maxpossiblecols+1):n]] - end - A = alignment_std(io, X, ldims, rdims, rowsA, colsA, screenwidth, screenwidth, sepsize) - # Nine-slicing is accomplished using print_matrix_row_std repeatedly - if m <= screenheight # rows fit vertically on screen - if n <= length(A) # rows and cols fit so just print whole matrix in one piece - for i in rowsA - print(io, i == first(rowsA) ? pre : presp) - print_matrix_row_std(io, X,A,ldims,rdims,i,colsA,sep) - print(io, i == last(rowsA) ? post : postsp) - if i != last(rowsA); println(io); end - end - else # rows fit down screen but cols don't, so need horizontal ellipsis - c = div(screenwidth-length(hdots)+1,2)+1 # what goes to right of ellipsis - Ralign = reverse(alignment_std(io, X, ldims, rdims, rowsA, reverse(colsA), c, c, sepsize)) # alignments for right - c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) - Lalign = alignment_std(io, X, ldims, rdims, rowsA, colsA, c, c, sepsize) # alignments for left of ellipsis - for i in rowsA - print(io, i == first(rowsA) ? pre : presp) - print_matrix_row_std(io, X,Lalign,ldims,rdims,i,colsA[1:length(Lalign)],sep) - print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) - print_matrix_row_std(io, X,Ralign,ldims,rdims,i,n-length(Ralign)+colsA,sep) - print(io, i == last(rowsA) ? post : postsp) - if i != last(rowsA); println(io); end - end - end - else # rows don't fit so will need vertical ellipsis - if n <= length(A) # rows don't fit, cols do, so only vertical ellipsis - for i in rowsA - print(io, i == first(rowsA) ? pre : presp) - print_matrix_row_std(io, X,A,ldims, rdims,i,colsA,sep) - print(io, i == last(rowsA) ? post : postsp) - if i != rowsA[end]; println(io); end - if i == rowsA[halfheight] - print(io, i == first(rowsA) ? pre : presp) - Base.print_matrix_vdots(io, vdots,A,sep,vmod,1) - println(io, i == last(rowsA) ? post : postsp) - end - end - else # neither rows nor cols fit, so use all 3 kinds of dots - c = div(screenwidth-length(hdots)+1,2)+1 - Ralign = reverse(alignment_std(io, X, ldims, rdims, rowsA, reverse(colsA), c, c, sepsize)) - c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) - Lalign = alignment_std(io, X, ldims, rdims, rowsA, colsA, c, c, sepsize) - r = mod((length(Ralign)-n+1),vmod) # where to put dots on right half - for i in rowsA - print(io, i == first(rowsA) ? pre : presp) - print_matrix_row_std(io, X,Lalign,ldims, rdims,i,colsA[1:length(Lalign)],sep) - print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) - print_matrix_row_std(io, X,Ralign,ldims, rdims,i,n-length(Ralign)+colsA,sep) - print(io, i == last(rowsA) ? post : postsp) - if i != rowsA[end]; println(io); end - if i == rowsA[halfheight] - print(io, i == first(rowsA) ? pre : presp) - Base.print_matrix_vdots(io, vdots,Lalign,sep,vmod,1) - print(io, ddots) - Base.print_matrix_vdots(io, vdots,Ralign,sep,vmod,r) - println(io, i == last(rowsA) ? post : postsp) - end - end - end - end -end - -function showarray_stdord(io::IO, X::AbstractVecOrMat, ldims::Vector, rdims::Vector, repr::Bool = true; header = true) - if !haskey(io, :compact) - io = IOContext(io, :compact => true) - end - if !isempty(X) - punct = (" ", " ", "") - print_matrix_std(io, X, ldims, rdims, punct...) - end -end -showarray_stdord(io::IO, X::Vector, dims::Vector, repr::Bool = true; header = true) = showarray_stdord(io, X, dims, [1], repr; header = header) - - -function showsparsearray_stdord(io::IO, S::SparseMatrixCSC, ldims::Vector, rdims::Vector) - if get(io, :limit, false) - rows = displaysize(io)[1] - half_screen_rows = div(rows - 8, 2) - else - half_screen_rows = typemax(Int) - end - pad = ndigits(max(S.m,S.n)) - sep = "\n " - if !haskey(io, :compact) - io = IOContext(io, :compact => true) - end - - colval = zeros(Int, nnz(S)) - for col in 1:S.n, k in S.colptr[col] : (S.colptr[col+1]-1) - colval[k] = col - end - rowval = S.rowval - rowval_std = map(x -> mirror_world_index(x, reverse(ldims)), rowval) - colval_std = map(x -> mirror_world_index(x, reverse(rdims)), colval) - idx_nzval = map((x,y,z) -> (x,y,z), rowval_std, colval_std, S.nzval) - sort!(idx_nzval, by = x -> (x[2], x[1]) ) - rowval_std = map(x -> x[1], idx_nzval) - colval_std = map(x -> x[2], idx_nzval) - nzval_std = map(x -> x[3], idx_nzval) - - for (k, val) in enumerate(nzval_std) - if k < half_screen_rows || k > nnz(S)-half_screen_rows - print(io, sep, '[', rpad(rowval_std[k], pad), ", ", lpad(colval_std[k], pad), "] = ") - show(io, nzval_std[k]) - elseif k == half_screen_rows - print(io, sep, '\u22ee') - end - end -end - -end # module diff --git a/src/schroedinger.jl b/src/schroedinger.jl index 439eee69..392933e5 100644 --- a/src/schroedinger.jl +++ b/src/schroedinger.jl @@ -1,12 +1,3 @@ -module timeevolution_schroedinger - -export schroedinger, schroedinger_dynamic - -import ..integrate, ..recast! - -using ...bases, ...states, ...operators - - """ timeevolution.schroedinger(tspan, psi0, H; fout) @@ -21,11 +12,10 @@ Integrate Schroedinger equation. normalized nor permanent! It is still in use by the ode solver and therefore must not be changed. """ -function schroedinger(tspan, psi0::T, H::Operator; - fout::Union{Function,Void}=nothing, - kwargs...) where T<:StateVector +function schroedinger(tspan, psi0::T, H::AbstractOperator{B,B}; + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:StateVector{B}} tspan_ = convert(Vector{Float64}, tspan) - check_schroedinger(psi0, H) dschroedinger_(t::Float64, psi::T, dpsi::T) = dschroedinger(psi, H, dpsi) x0 = psi0.data state = T(psi0.basis, psi0.data) @@ -49,7 +39,7 @@ Integrate time-dependent Schroedinger equation. therefore must not be changed. """ function schroedinger_dynamic(tspan, psi0::T, f::Function; - fout::Union{Function,Void}=nothing, + fout::Union{Function,Nothing}=nothing, kwargs...) where T<:StateVector tspan_ = convert(Vector{Float64}, tspan) dschroedinger_(t::Float64, psi::T, dpsi::T) = dschroedinger_dynamic(t, psi, f, dpsi) @@ -60,36 +50,33 @@ function schroedinger_dynamic(tspan, psi0::T, f::Function; end -recast!(x::Vector{Complex128}, psi::StateVector) = (psi.data = x); -recast!(psi::StateVector, x::Vector{Complex128}) = nothing +recast!(x::D, psi::StateVector{B,D}) where {B<:Basis, D<:Vector{ComplexF64}} = (psi.data = x); +recast!(psi::StateVector{B,D}, x::D) where {B<:Basis, D<:Vector{ComplexF64}} = nothing -function dschroedinger(psi::Ket, H::Operator, dpsi::Ket) - operators.gemv!(complex(0,-1.), H, psi, complex(0.), dpsi) +function dschroedinger(psi::Ket{B}, H::AbstractOperator{B,B}, dpsi::Ket{B}) where B<:Basis + QuantumOpticsBase.gemv!(complex(0,-1.), H, psi, complex(0.), dpsi) return dpsi end -function dschroedinger(psi::Bra, H::Operator, dpsi::Bra) - operators.gemv!(complex(0,1.), psi, H, complex(0.), dpsi) +function dschroedinger(psi::Bra{B}, H::AbstractOperator{B,B}, dpsi::Bra{B}) where B<:Basis + QuantumOpticsBase.gemv!(complex(0,1.), psi, H, complex(0.), dpsi) return dpsi end function dschroedinger_dynamic(t::Float64, psi0::T, f::Function, dpsi::T) where T<:StateVector H = f(t, psi0) - check_schroedinger(psi0, H) dschroedinger(psi0, H, dpsi) end -function check_schroedinger(psi::Ket, H::Operator) +function check_schroedinger(psi::Ket, H::AbstractOperator) check_multiplicable(H, psi) check_samebases(H) end -function check_schroedinger(psi::Bra, H::Operator) +function check_schroedinger(psi::Bra, H::AbstractOperator) check_multiplicable(psi, H) check_samebases(H) end - -end diff --git a/src/semiclassical.jl b/src/semiclassical.jl index b417a529..c2144323 100644 --- a/src/semiclassical.jl +++ b/src/semiclassical.jl @@ -1,14 +1,22 @@ module semiclassical +using QuantumOpticsBase import Base: == -import ..bases, ..operators, ..operators_dense -import ..timeevolution: integrate, recast! +import ..timeevolution: integrate, recast!, jump, integrate_mcwf, jump_callback, as_vector, QO_CHECKS +import LinearAlgebra: normalize, normalize! -using ..bases, ..states, ..operators, ..operators_dense, ..timeevolution +using Random, LinearAlgebra +import OrdinaryDiffEq +# TODO: Remove imports +import DiffEqCallbacks, RecursiveArrayTools.copyat_or_push! +Base.@pure pure_inference(fout,T) = Core.Compiler.return_type(fout, T) -const QuantumState = Union{Ket, DenseOperator} -const DecayRates = Union{Void, Vector{Float64}, Matrix{Float64}} +using ..timeevolution + + +const QuantumState{B} = Union{Ket{B}, DenseOperator{B,B}} +const DecayRates = Union{Nothing, Vector{Float64}, Matrix{Float64}} """ Semi-classical state. @@ -16,26 +24,31 @@ Semi-classical state. It consists of a quantum part, which is either a `Ket` or a `DenseOperator` and a classical part that is specified as a complex vector of arbitrary length. """ -mutable struct State{T<:QuantumState} +mutable struct State{B<:Basis,T<:QuantumState{B},C<:Vector{ComplexF64}} quantum::T - classical::Vector{Complex128} + classical::C + function State(quantum::T, classical::C) where {B<:Basis,T<:QuantumState{B},C<:Vector{ComplexF64}} + new{B,T,C}(quantum, classical) + end end Base.length(state::State) = length(state.quantum) + length(state.classical) Base.copy(state::State) = State(copy(state.quantum), copy(state.classical)) +normalize!(state::State{B,T}) where {B,T<:Ket} = normalize!(state.quantum) +normalize(state::T) where {B,K<:Ket,T<:State{B,K}} = State(normalize(state.quantum),copy(state.classical)) -function ==(a::State{T}, b::State{T}) where T<:QuantumState - samebases(a.quantum, b.quantum) && +function ==(a::State, b::State) + QuantumOpticsBase.samebases(a.quantum, b.quantum) && length(a.classical)==length(b.classical) && (a.classical==b.classical) && (a.quantum==b.quantum) end -operators.expect(op, state::State) = expect(op, state.quantum) -operators.variance(op, state::State) = variance(op, state.quantum) -operators.ptrace(state::State, indices::Vector{Int}) = State{DenseOperator}(ptrace(state.quantum, indices), state.classical) +QuantumOpticsBase.expect(op, state::State) = expect(op, state.quantum) +QuantumOpticsBase.variance(op, state::State) = variance(op, state.quantum) +QuantumOpticsBase.ptrace(state::State, indices::Vector{Int}) = State(ptrace(state.quantum, indices), state.classical) -operators_dense.dm(x::State{Ket}) = State{DenseOperator}(dm(x.quantum), x.classical) +QuantumOpticsBase.dm(x::State{B,T}) where {B<:Basis,T<:Ket{B}} = State(dm(x.quantum), x.classical) """ @@ -57,12 +70,12 @@ Integrate time-dependent Schrödinger equation coupled to a classical system. normalized nor permanent! * `kwargs...`: Further arguments are passed on to the ode solver. """ -function schroedinger_dynamic(tspan, state0::State{Ket}, fquantum::Function, fclassical::Function; - fout::Union{Function,Void}=nothing, - kwargs...) +function schroedinger_dynamic(tspan, state0::S, fquantum::Function, fclassical::Function; + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:Ket{B},S<:State{B,T}} tspan_ = convert(Vector{Float64}, tspan) - dschroedinger_(t, state::State{Ket}, dstate::State{Ket}) = dschroedinger_dynamic(t, state, fquantum, fclassical, dstate) - x0 = Vector{Complex128}(length(state0)) + dschroedinger_(t::Float64, state::S, dstate::S) = dschroedinger_dynamic(t, state, fquantum, fclassical, dstate) + x0 = Vector{ComplexF64}(undef, length(state0)) recast!(state0, x0) state = copy(state0) dstate = copy(state0) @@ -88,52 +101,151 @@ Integrate time-dependent master equation coupled to a classical system. permanent! * `kwargs...`: Further arguments are passed on to the ode solver. """ -function master_dynamic(tspan, state0::State{DenseOperator}, fquantum, fclassical; - rates::Union{Vector{Float64}, Matrix{Float64}, Void}=nothing, - fout::Union{Function,Void}=nothing, - tmp::DenseOperator=copy(state0.quantum), - kwargs...) +function master_dynamic(tspan, state0::State{B,T}, fquantum, fclassical; + rates::DecayRates=nothing, + fout::Union{Function,Nothing}=nothing, + tmp::T=copy(state0.quantum), + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} tspan_ = convert(Vector{Float64}, tspan) - function dmaster_(t, state::State{DenseOperator}, dstate::State{DenseOperator}) + function dmaster_(t::Float64, state::S, dstate::S) where {B<:Basis,T<:DenseOperator{B,B},S<:State{B,T}} dmaster_h_dynamic(t, state, fquantum, fclassical, rates, dstate, tmp) end - x0 = Vector{Complex128}(length(state0)) + x0 = Vector{ComplexF64}(undef, length(state0)) recast!(state0, x0) state = copy(state0) dstate = copy(state0) integrate(tspan_, dmaster_, x0, state, dstate, fout; kwargs...) end -function master_dynamic(tspan, state0::State{Ket}, fquantum, fclassical; kwargs...) +function master_dynamic(tspan, state0::State{B,T}, fquantum, fclassical; kwargs...) where {B<:Basis,T<:Ket{B}} master_dynamic(tspan, dm(state0), fquantum, fclassical; kwargs...) end +""" + semiclassical.mcwf_dynamic(tspan, psi0, fquantum, fclassical, fjump_classical; ) + +Calculate MCWF trajectories coupled to a classical system. -function recast!(state::State, x::Vector{Complex128}) +# Arguments +* `tspan`: Vector specifying the points of time for which output should + be displayed. +* `psi0`: Initial semi-classical state [`semiclassical.State`](@ref) featuring + a `Ket`(@ref). +* `fquantum`: Function `f(t, psi, u) -> (H, J, Jdagger)` returning the time + and/or state dependent Hamiltonian and Jump operators. +* `fclassical`: Function `f(t, psi, u, du)` calculating the possibly time and + state dependent derivative of the classical equations and storing it + in the complex vector `du`. +* `fjump_classical`: Function `f(t, psi, u, i)` performing a classical jump when a + quantum jump of the i-th jump operator occurs. +* `fout=nothing`: If given, this function `fout(t, state)` is called every time + an output should be displayed. ATTENTION: The given state is not + permanent! +* `display_beforeevent`: Choose whether or not an additional point should be saved + before a jump occurs. Default is false. +* `display_afterevent`: Choose whether or not an additional point should be saved + after a jump occurs. Default is false. +* `display_jumps=false`: If set to true, an additional list of times and indices + is returned. These correspond to the times at which a jump occured and + the index of the jump operators with which the jump occured, respectively. +* `kwargs...`: Further arguments are passed on to the ode solver. +""" +function mcwf_dynamic(tspan, psi0::State{B,T}, fquantum, fclassical, fjump_classical; + seed=rand(UInt), + rates::DecayRates=nothing, + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:Ket{B}} + tspan_ = convert(Vector{Float64}, tspan) + tmp=copy(psi0.quantum) + function dmcwf_(t::Float64, psi::S, dpsi::S) where {B<:Basis,T<:Ket{B},S<:State{B,T}} + dmcwf_h_dynamic(t, psi, fquantum, fclassical, rates, dpsi, tmp) + end + j_(rng, t::Float64, psi, psi_new) = jump_dynamic(rng, t, psi, fquantum, fclassical, fjump_classical, psi_new, rates) + x0 = Vector{ComplexF64}(undef, length(psi0)) + recast!(psi0, x0) + psi = copy(psi0) + dpsi = copy(psi0) + integrate_mcwf(dmcwf_, j_, tspan_, psi, seed, fout; kwargs...) +end + +function recast!(state::State{B,T,C}, x::C) where {B<:Basis,T<:QuantumState{B},C<:Vector{ComplexF64}} N = length(state.quantum) - copy!(x, 1, state.quantum.data, 1, N) - copy!(x, N+1, state.classical, 1, length(state.classical)) + copyto!(x, 1, state.quantum.data, 1, N) + copyto!(x, N+1, state.classical, 1, length(state.classical)) x end -function recast!(x::Vector{Complex128}, state::State) +function recast!(x::C, state::State{B,T,C}) where {B<:Basis,T<:QuantumState{B},C<:Vector{ComplexF64}} N = length(state.quantum) - copy!(state.quantum.data, 1, x, 1, N) - copy!(state.classical, 1, x, N+1, length(state.classical)) + copyto!(state.quantum.data, 1, x, 1, N) + copyto!(state.classical, 1, x, N+1, length(state.classical)) end -function dschroedinger_dynamic(t::Float64, state::State{Ket}, fquantum::Function, - fclassical::Function, dstate::State{Ket}) +function dschroedinger_dynamic(t::Float64, state::State{B,T}, fquantum::Function, + fclassical::Function, dstate::State{B,T}) where {B<:Basis,T<:Ket{B}} fquantum_(t, psi) = fquantum(t, state.quantum, state.classical) - timeevolution.timeevolution_schroedinger.dschroedinger_dynamic(t, state.quantum, fquantum_, dstate.quantum) + timeevolution.dschroedinger_dynamic(t, state.quantum, fquantum_, dstate.quantum) fclassical(t, state.quantum, state.classical, dstate.classical) end -function dmaster_h_dynamic(t::Float64, state::State{DenseOperator}, fquantum::Function, - fclassical::Function, rates::DecayRates, dstate::State{DenseOperator}, tmp::DenseOperator) +function dmaster_h_dynamic(t::Float64, state::State{B,T}, fquantum::Function, + fclassical::Function, rates::DecayRates, dstate::State{B,T}, tmp::T) where {B<:Basis,T<:DenseOperator{B,B}} fquantum_(t, rho) = fquantum(t, state.quantum, state.classical) - timeevolution.timeevolution_master.dmaster_h_dynamic(t, state.quantum, fquantum_, rates, dstate.quantum, tmp) + timeevolution.dmaster_h_dynamic(t, state.quantum, fquantum_, rates, dstate.quantum, tmp) fclassical(t, state.quantum, state.classical, dstate.classical) end +function dmcwf_h_dynamic(t::Float64, psi::T, fquantum::Function, fclassical::Function, rates::DecayRates, + dpsi::T, tmp::K) where {T,K} + fquantum_(t, rho) = fquantum(t, psi.quantum, psi.classical) + timeevolution.dmcwf_h_dynamic(t, psi.quantum, fquantum_, rates, dpsi.quantum, tmp) + fclassical(t, psi.quantum, psi.classical, dpsi.classical) +end + +function jump_dynamic(rng, t::Float64, psi::T, fquantum::Function, fclassical::Function, fjump_classical::Function, psi_new::T, rates::DecayRates) where T<:State + result = fquantum(t, psi.quantum, psi.classical) + QO_CHECKS[] && @assert 3 <= length(result) <= 4 + J = result[2] + if length(result) == 3 + rates_ = rates + else + rates_ = result[4] + end + i = jump(rng, t, psi.quantum, J, psi_new.quantum, rates_) + fjump_classical(t, psi_new.quantum, psi.classical, i) + psi_new.classical .= psi.classical + return i +end + +function jump_callback(jumpfun::Function, seed, scb, save_before!::Function, + save_after!::Function, save_t_index::Function, psi0::State) + tmp = copy(psi0) + psi_tmp = copy(psi0) + rng = MersenneTwister(convert(UInt, seed)) + jumpnorm = Ref(rand(rng)) + n = length(psi0.quantum) + djumpnorm(x::Vector{ComplexF64}, t::Float64, integrator) = norm(x[1:n])^2 - (1-jumpnorm[]) + + function dojump(integrator) + x = integrator.u + t = integrator.t + + affect! = scb.affect! + save_before!(affect!,integrator) + recast!(x, psi_tmp) + i = jumpfun(rng, t, psi_tmp, tmp) + recast!(tmp, x) + save_after!(affect!,integrator) + save_t_index(t,i) + + jumpnorm[] = rand(rng) + return nothing + end + + return OrdinaryDiffEq.ContinuousCallback(djumpnorm,dojump, + save_positions = (false,false)) +end +as_vector(psi::State{B,K}) where {B,K<:Ket} = [psi.quantum.data; psi.classical] + + end # module diff --git a/src/sortedindices.jl b/src/sortedindices.jl deleted file mode 100644 index 9dc26994..00000000 --- a/src/sortedindices.jl +++ /dev/null @@ -1,179 +0,0 @@ -module sortedindices - -""" -6, [1, 4] => [2, 3, 5, 6] -""" -function complement(N::Int, indices::Vector{Int}) - L = length(indices) - x = Vector{Int}(N - L) - i_ = 1 # Position in the x vector - j = 1 # Position in indices vector - for i=1:N - if j > L || indices[j]!=i - x[i_] = i - i_ += 1 - else - j += 1 - end - end - x -end - -""" -[1, 4, 5], [2, 4, 7] => [1, 2, 4, 5, 7] -""" -function union(ind1::Vector{Int}, ind2::Vector{Int}) - i1 = 1 - i2 = 1 - N1 = length(ind1) - N2 = length(ind2) - xvec = Vector{Int}() - while true - if i1 > N1 - for j=i2:N2 - push!(xvec, ind2[j]) - end - return xvec - elseif i2 > N2 - for j=i1:N1 - push!(xvec, ind1[j]) - end - return xvec - end - x1 = ind1[i1] - x2 = ind2[i2] - if x1 == x2 - i1 += 1 - i2 += 1 - push!(xvec, x1) - elseif x1 < x2 - i1 += 1 - push!(xvec, x1) - else - i2 += 1 - push!(xvec, x2) - end - end -end - - -""" -[1, 4, 5], [2, 4, 7] => [1, 5] -""" -function remove(ind1::Vector{Int}, ind2::Vector{Int}) - x = Int[] - for i in ind1 - if i ∉ ind2 - push!(x, i) - end - end - x -end - -""" -[1, 4, 5], [2, 4, 7] => [1, 3] -""" -function shiftremove(ind1::Vector{Int}, ind2::Vector{Int}) - x = Int[] - for i in ind1 - if i ∉ ind2 - counter = 0 - for i2 in ind2 - if i2 < i - counter += 1 - else - break - end - end - push!(x, i-counter) - end - end - x -end - -""" -[2, 3, 6], [1, 3, 4, 6, 7] => [3, 6] -""" -function intersect(ind1::Vector{Int}, ind2::Vector{Int}) - i1 = 1 - i2 = 1 - N1 = length(ind1) - N2 = length(ind2) - xvec = Vector{Int}() - if i1 > N1 || i2 > N2 - return xvec - end - x1 = ind1[i1] - x2 = ind2[i2] - while true - if x1 == x2 - i1 += 1 - i2 += 1 - push!(xvec, x1) - if i1 > N1 || i2 > N2 - return xvec - end - x1 = ind1[i1] - x2 = ind2[i2] - elseif x1 < x2 - i1 += 1 - if i1 > N1 - return xvec - end - x1 = ind1[i1] - else - i2 += 1 - if i2 > N2 - return xvec - end - x2 = ind2[i2] - end - end -end - -function reducedindices(I_::Vector{Int}, I::Vector{Int}) - N = length(I_) - x = Vector{Int}(N) - for n in 1:N - x[n] = findfirst(I, I_[n]) - end - x -end - -function reducedindices!(I_::Vector{Int}, I::Vector{Int}) - for n in 1:length(I_) - I_[n] = findfirst(I, I_[n]) - end -end - -""" -Check if all indices are unique and smaller than or equal to imax. -""" -function check_indices(imax::Int, indices::Vector{Int}) - N = length(indices) - for n=1:N - i = indices[n] - @assert 0 < i <= imax - for m in n+1:N - @assert i != indices[m] - end - end -end - -""" -Check if the indices are sorted, unique and smaller than or equal to imax. -""" -function check_sortedindices(imax::Int, indices::Vector{Int}) - N = length(indices) - if N == 0 - return nothing - end - i_ = indices[1] - @assert 0 < i_ <= imax - for i in indices[2:end] - @assert 0 < i <= imax - @assert i > i_ - end -end - -end # module diff --git a/src/sparsematrix.jl b/src/sparsematrix.jl deleted file mode 100644 index 2211b5cc..00000000 --- a/src/sparsematrix.jl +++ /dev/null @@ -1,180 +0,0 @@ -module sparsematrix - -const SparseMatrix = SparseMatrixCSC{Complex128, Int} - - -function gemm_sp_dense_small(alpha::Complex128, M::SparseMatrix, B::Matrix{Complex128}, result::Matrix{Complex128}) - if alpha == Complex128(1.) - @inbounds for colindex = 1:M.n - @inbounds for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - row = M.rowval[i] - val = M.nzval[i] - @inbounds for j=1:size(B, 2) - result[row, j] += val*B[colindex, j] - end - end - end - else - @inbounds for colindex = 1:M.n - @inbounds for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - row = M.rowval[i] - val = alpha*M.nzval[i] - @inbounds for j=1:size(B, 2) - result[row, j] += val*B[colindex, j] - end - end - end - end -end - -function gemm_sp_dense_big(alpha::Complex128, M::SparseMatrix, B::Matrix{Complex128}, result::Matrix{Complex128}) - if alpha == Complex128(1.) - @inbounds for j=1:size(B, 2) - @inbounds for colindex = 1:M.n - m2 = B[colindex, j] - @inbounds for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - row = M.rowval[i] - result[row, j] += M.nzval[i]*m2 - end - end - end - else - @inbounds for j=1:size(B, 2) - @inbounds for colindex = 1:M.n - m2 = alpha*B[colindex, j] - @inbounds for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - row = M.rowval[i] - result[row, j] += M.nzval[i]*m2 - end - end - end - end -end - -function gemm!(alpha::Complex128, M::SparseMatrix, B::Matrix{Complex128}, beta::Complex128, result::Matrix{Complex128}) - if beta == Complex128(0.) - fill!(result, beta) - elseif beta != Complex128(1.) - scale!(result, beta) - end - if nnz(M) > 1000 - gemm_sp_dense_big(alpha, M, B, result) - else - gemm_sp_dense_small(alpha, M, B, result) - end -end - -function gemm!(alpha::Complex128, B::Matrix{Complex128}, M::SparseMatrix, beta::Complex128, result::Matrix{Complex128}) - if beta == Complex128(0.) - fill!(result, beta) - elseif beta != Complex128(1.) - scale!(result, beta) - end - dimB = size(result,1) - if alpha == Complex128(1.) - @inbounds for colindex = 1:M.n - @inbounds for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - mi = M.nzval[i] - mrowvali = M.rowval[i] - @inbounds for j=1:dimB - result[j, colindex] += mi*B[j, mrowvali] - end - end - end - else - @inbounds for colindex = 1:M.n - @inbounds for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - mi = M.nzval[i]*alpha - mrowvali = M.rowval[i] - @inbounds for j=1:dimB - result[j, colindex] += mi*B[j, mrowvali] - end - end - end - end -end - -function gemv!(alpha::Complex128, M::SparseMatrix, v::Vector{Complex128}, beta::Complex128, result::Vector{Complex128}) - if beta == Complex128(0.) - fill!(result, beta) - elseif beta != Complex128(1.) - scale!(result, beta) - end - if alpha == Complex128(1.) - @inbounds for colindex = 1:M.n - vj = v[colindex] - for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - result[M.rowval[i]] += M.nzval[i]*vj - end - end - else - @inbounds for colindex = 1:M.n - vj = alpha*v[colindex] - for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - result[M.rowval[i]] += M.nzval[i]*vj - end - end - end -end - -function gemv!(alpha::Complex128, v::Vector{Complex128}, M::SparseMatrix, beta::Complex128, result::Vector{Complex128}) - if beta == Complex128(0.) - fill!(result, beta) - elseif beta != Complex128(1.) - scale!(result, beta) - end - if alpha == Complex128(1.) - @inbounds for colindex=1:M.n - for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - result[colindex] += M.nzval[i]*v[M.rowval[i]] - end - end - else - @inbounds for colindex=1:M.n - for i=M.colptr[colindex]:M.colptr[colindex+1]-1 - result[colindex] += M.nzval[i]*alpha*v[M.rowval[i]] - end - end - end -end - -function sub2sub(shape1::NTuple{N, Int}, shape2::NTuple{M, Int}, I::CartesianIndex{N}) where {N, M} - linearindex = sub2ind(shape1, I.I...) - CartesianIndex(ind2sub(shape2, linearindex)...) -end - -function ptrace(x, shape_nd::Vector{Int}, indices::Vector{Int}) - shape_nd = (shape_nd...) - N = div(length(shape_nd), 2) - shape_2d = (x.m, x.n) - shape_nd_after = ([i ∈ indices || i-N ∈ indices ? 1 : shape_nd[i] for i=1:2*N]...) - shape_2d_after = (prod(shape_nd_after[1:N]), prod(shape_nd_after[N+1:end])) - I_nd_after_max = CartesianIndex(shape_nd_after...) - y = spzeros(Complex128, shape_2d_after...) - for I in eachindex(x) - I_nd = sub2sub(shape_2d, shape_nd, I) - if I_nd.I[indices] != I_nd.I[indices + N] - continue - end - I_after = sub2sub(shape_nd_after, shape_2d_after, min(I_nd, I_nd_after_max)) - y[I_after] += x[I] - end - y -end - -function permutedims(x, shape, perm) - shape = (shape...) - shape_perm = ([shape[i] for i in perm]...) - y = spzeros(Complex128, x.m, x.n) - for I in eachindex(x) - linear_index = sub2ind((x.m, x.n), I.I...) - cartesian_index = ind2sub(shape, linear_index) - cartesian_index_perm = [cartesian_index[i] for i=perm] - linear_index_perm = sub2ind(shape_perm, cartesian_index_perm...) - J = ind2sub((x.m, x.n), linear_index_perm) - y[J...] = x[I.I...] - end - y -end - -end # module diff --git a/src/spectralanalysis.jl b/src/spectralanalysis.jl index be90450c..e0ad8c75 100644 --- a/src/spectralanalysis.jl +++ b/src/spectralanalysis.jl @@ -1,26 +1,21 @@ -module spectralanalysis - -export eigenstates, eigenenergies, simdiag - -using ..bases, ..states, ..operators, ..operators_dense, ..operators_sparse - +using Arpack const nonhermitian_warning = "The given operator is not hermitian. If this is due to a numerical error make the operator hermitian first by calculating (x+dagger(x))/2 first." """ - eigenstates(op::Operator[, n::Int; warning=true]) + eigenstates(op::AbstractOperator[, n::Int; warning=true]) Calculate the lowest n eigenvalues and their corresponding eigenstates. -This is just a thin wrapper around julia's `eig` and `eigs` functions. Which +This is just a thin wrapper around julia's `eigen` and `eigs` functions. Which of them is used depends on the type of the given operator. If more control about the way the calculation is done is needed, use the functions directly. More details can be found at [http://docs.julialang.org/en/stable/stdlib/linalg/]. -NOTE: Especially for small systems full diagonalization with Julia's `eig` +NOTE: Especially for small systems full diagonalization with Julia's `eigen` function is often more desirable. You can convert a sparse operator `A` to a -dense one using `full(A)`. +dense one using `dense(A)`. If the given operator is non-hermitian a warning is given. This behavior can be turned off using the keyword `warning=false`. @@ -28,12 +23,12 @@ can be turned off using the keyword `warning=false`. function eigenstates(op::DenseOperator, n::Int=length(basis(op)); warning=true) b = basis(op) if ishermitian(op) - D, V = eig(Hermitian(op.data), 1:n) + D, V = eigen(Hermitian(op.data), 1:n) states = [Ket(b, V[:, k]) for k=1:length(D)] return D, states else - warning && warn(nonhermitian_warning) - D, V = eig(op.data) + warning && @warn(nonhermitian_warning) + D, V = eigen(op.data) states = [Ket(b, V[:, k]) for k=1:length(D)] perm = sortperm(D, by=real) permute!(D, perm) @@ -49,10 +44,10 @@ function eigenstates(op::SparseOperator, n::Int=6; warning::Bool=true, info::Bool=true, kwargs...) b = basis(op) # TODO: Change to sparese-Hermitian specific algorithm if more efficient - ishermitian(op) || (warning && warn(nonhermitian_warning)) + ishermitian(op) || (warning && @warn(nonhermitian_warning)) info && println("INFO: Defaulting to sparse diagonalization. If storing the full operator is possible, it might be faster to do - eigenstates(full(op)). Set info=false to turn off this message.") + eigenstates(dense(op)). Set info=false to turn off this message.") D, V = eigs(op.data; which=:SR, nev=n, kwargs...) states = [Ket(b, V[:, k]) for k=1:length(D)] D, states @@ -60,7 +55,7 @@ end """ - eigenenergies(op::Operator[, n::Int; warning=true]) + eigenenergies(op::AbstractOperator[, n::Int; warning=true]) Calculate the lowest n eigenvalues. @@ -78,7 +73,7 @@ function eigenenergies(op::DenseOperator, n::Int=length(basis(op)); warning=true D = eigvals(Hermitian(op.data), 1:n) return D else - warning && warn(nonhermitian_warning) + warning && @warn(nonhermitian_warning) D = eigvals(op.data) sort!(D, by=real) return D[1:n] @@ -91,9 +86,9 @@ For sparse operators by default it only returns the 6 lowest eigenvalues. eigenenergies(op::SparseOperator, n::Int=6; kwargs...) = eigenstates(op, n; kwargs...)[1] -arithmetic_unary_error = operators.arithmetic_unary_error -eigenstates(op::Operator, n::Int=0) = arithmetic_unary_error("eigenstates", op) -eigenenergies(op::Operator, n::Int=0) = arithmetic_unary_error("eigenenergies", op) +arithmetic_unary_error = QuantumOpticsBase.arithmetic_unary_error +eigenstates(op::AbstractOperator, n::Int=0) = arithmetic_unary_error("eigenstates", op) +eigenenergies(op::AbstractOperator, n::Int=0) = arithmetic_unary_error("eigenenergies", op) """ @@ -125,9 +120,9 @@ function simdiag(ops::Vector{T}; atol::Real=1e-14, rtol::Real=1e-14) where T<:De end end - d, v = eig(sum(ops).data) + d, v = eigen(sum(ops).data) - evals = [Vector{Complex128}(length(d)) for i=1:length(ops)] + evals = [Vector{ComplexF64}(undef, length(d)) for i=1:length(ops)] for i=1:length(ops), j=1:length(d) vec = ops[i].data*v[:, j] evals[i][j] = (v[:, j]'*vec)[1] @@ -140,5 +135,3 @@ function simdiag(ops::Vector{T}; atol::Real=1e-14, rtol::Real=1e-14) where T<:De evals_sorted = [real(evals[i][index]) for i=1:length(ops)] return evals_sorted, v[:, index] end - -end # module diff --git a/src/spin.jl b/src/spin.jl deleted file mode 100644 index 400ff686..00000000 --- a/src/spin.jl +++ /dev/null @@ -1,114 +0,0 @@ -module spin - -export SpinBasis, sigmax, sigmay, sigmaz, sigmap, sigmam, spinup, spindown - -import Base: == - -using Compat -using ..bases, ..states, ..operators, ..operators_sparse - - -""" - SpinBasis(n) - -Basis for spin-n particles. - -The basis can be created for arbitrary spinnumbers by using a rational number, -e.g. `SpinBasis(3//2)`. The Pauli operators are defined for all possible -spin numbers. -""" -mutable struct SpinBasis <: Basis - shape::Vector{Int} - spinnumber::Rational{Int} - function SpinBasis(spinnumber::Rational{Int}) - n = numerator(spinnumber) - d = denominator(spinnumber) - @assert d==2 || d==1 - @assert n > 0 - N = numerator(spinnumber*2 + 1) - new([N], spinnumber) - end -end -SpinBasis(spinnumber::Int) = SpinBasis(convert(Rational{Int}, spinnumber)) - -==(b1::SpinBasis, b2::SpinBasis) = b1.spinnumber==b2.spinnumber - -""" - sigmax(b::SpinBasis) - -Pauli ``σ_x`` operator for the given Spin basis. -""" -function sigmax(b::SpinBasis) - N = length(b) - diag = Complex128[complex(sqrt(real((b.spinnumber + 1)*2*a - a*(a+1)))) for a=1:N-1] - data = spdiagm(diag, 1, N, N) + spdiagm(diag, -1, N, N) - SparseOperator(b, data) -end - -""" - sigmay(b::SpinBasis) - -Pauli ``σ_y`` operator for the given Spin basis. -""" -function sigmay(b::SpinBasis) - N = length(b) - diag = Complex128[1im*complex(sqrt(real((b.spinnumber + 1)*2*a - a*(a+1)))) for a=1:N-1] - data = spdiagm(diag, -1, N, N) - spdiagm(diag, 1, N, N) - SparseOperator(b, data) -end - -""" - sigmaz(b::SpinBasis) - -Pauli ``σ_z`` operator for the given Spin basis. -""" -function sigmaz(b::SpinBasis) - N = length(b) - diag = Complex128[complex(2*m) for m=b.spinnumber:-1:-b.spinnumber] - data = spdiagm(diag, 0, N, N) - SparseOperator(b, data) -end - -""" - sigmap(b::SpinBasis) - -Raising operator ``σ_+`` for the given Spin basis. -""" -function sigmap(b::SpinBasis) - N = length(b) - S = (b.spinnumber + 1)*b.spinnumber - diag = Complex128[complex(sqrt(float(S - m*(m+1)))) for m=b.spinnumber-1:-1:-b.spinnumber] - data = spdiagm(diag, 1, N, N) - SparseOperator(b, data) -end - -""" - sigmam(b::SpinBasis) - -Lowering operator ``σ_-`` for the given Spin basis. -""" -function sigmam(b::SpinBasis) - N = length(b) - S = (b.spinnumber + 1)*b.spinnumber - diag = [complex(sqrt(float(S - m*(m-1)))) for m=b.spinnumber:-1:-b.spinnumber+1] - data = spdiagm(diag, -1, N, N) - SparseOperator(b, data) -end - - -""" - spinup(b::SpinBasis) - -Spin up state for the given Spin basis. -""" -spinup(b::SpinBasis) = basisstate(b, 1) - -""" - spindown(b::SpinBasis) - -Spin down state for the given Spin basis. -""" -spindown(b::SpinBasis) = basisstate(b, b.shape[1]) - - -end #module diff --git a/src/state_definitions.jl b/src/state_definitions.jl deleted file mode 100644 index b0c98dd7..00000000 --- a/src/state_definitions.jl +++ /dev/null @@ -1,64 +0,0 @@ -module state_definitions - -export randstate, randoperator, thermalstate, coherentthermalstate, phase_average, passive_state - -using ..bases, ..states, ..operators, ..operators_dense, ..fock - - -""" - randstate(basis) - -Calculate a random normalized ket state. -""" -function randstate(b::Basis) - psi = Ket(b, rand(Complex128, length(b))) - normalize!(psi) - psi -end - -""" - randoperator(b1[, b2]) - -Calculate a random unnormalized dense operator. -""" -randoperator(b1::Basis, b2::Basis) = DenseOperator(b1, b2, rand(Complex128, length(b1), length(b2))) -randoperator(b::Basis) = randoperator(b, b) - -""" - thermalstate(H,T) - -Thermal state ``exp(-H/T)/Tr[exp(-H/T)]``. -""" -function thermalstate(H::Operator,T::Real) - return normalize(expm(-full(H)/T)) -end - -""" - coherentthermalstate(basis::FockBasis,H,T,alpha) - -Coherent thermal state ``D(α)exp(-H/T)/Tr[exp(-H/T)]D^†(α)``. -""" -function coherentthermalstate(basis::FockBasis,H::Operator,T::Real,alpha::Number) - return displace(basis,alpha)*thermalstate(H,T)*dagger(displace(basis,alpha)) -end - -""" - phase_average(rho) - -Returns the phase-average of ``ρ`` containing only the diagonal elements. -""" -function phase_average(rho::DenseOperator) - return DenseOperator(basis(rho),diagm(diag(rho.data))) -end - -""" - passive_state(rho,IncreasingEigenenergies::Bool=true) - -Passive state ``π`` of ``ρ``. IncreasingEigenenergies=true means that higher indices correspond to higher energies. -""" -function passive_state(rho::DenseOperator,IncreasingEigenenergies::Bool=true) - return DenseOperator(basis(rho),diagm(sort(abs.(eigvals(rho.data)),rev=IncreasingEigenenergies))) -end - -end #module - diff --git a/src/states.jl b/src/states.jl deleted file mode 100644 index 32dbe9a7..00000000 --- a/src/states.jl +++ /dev/null @@ -1,155 +0,0 @@ -module states - -export StateVector, Bra, Ket, length, basis, dagger, tensor, - norm, normalize, normalize!, permutesystems, basisstate - -import Base: ==, +, -, *, /, length, copy, norm, normalize, normalize! -import ..bases: basis, tensor, permutesystems, check_multiplicable, samebases - -using Compat -using ..bases - - -""" -Abstract base class for [`Bra`](@ref) and [`Ket`](@ref) states. - -The state vector class stores the coefficients of an abstract state -in respect to a certain basis. These coefficients are stored in the -`data` field and the basis is defined in the `basis` -field. -""" -abstract type StateVector end - -""" - Bra(b::Basis[, data]) - -Bra state defined by coefficients in respect to the basis. -""" -mutable struct Bra <: StateVector - basis::Basis - data::Vector{Complex128} - function Bra(b::Basis, data) - if length(b) != length(data) - throw(DimensionMismatch()) - end - new(b, data) - end -end - -""" - Ket(b::Basis[, data]) - -Ket state defined by coefficients in respect to the given basis. -""" -mutable struct Ket <: StateVector - basis::Basis - data::Vector{Complex128} - function Ket(b::Basis, data) - if length(b) != length(data) - throw(DimensionMismatch()) - end - new(b, data) - end -end - -Bra(b::Basis) = Bra(b, zeros(Complex128, length(b))) -Ket(b::Basis) = Ket(b, zeros(Complex128, length(b))) - -copy(a::T) where {T<:StateVector} = T(a.basis, copy(a.data)) -length(a::StateVector) = length(a.basis)::Int -basis(a::StateVector) = a.basis - -==(x::T, y::T) where {T<:StateVector} = samebases(x, y) && x.data==y.data - -# Arithmetic operations -+(a::T, b::T) where {T<:StateVector} = (check_samebases(a, b); T(a.basis, a.data+b.data)) - --(a::T) where {T<:StateVector} = T(a.basis, -a.data) --(a::T, b::T) where {T<:StateVector} = (check_samebases(a, b); T(a.basis, a.data-b.data)) - -*(a::Bra, b::Ket) = (check_multiplicable(a, b); sum(a.data.*b.data)) -*(a::Number, b::T) where {T<:StateVector} = T(b.basis, a*b.data) -*(a::T, b::Number) where {T<:StateVector} = T(a.basis, b*a.data) - -/(a::T, b::Number) where {T<:StateVector} = T(a.basis, a.data/b) - - -""" - dagger(x) - -Hermitian conjugate. -""" -dagger(x::Bra) = Ket(x.basis, conj(x.data)) -dagger(x::Ket) = Bra(x.basis, conj(x.data)) - -""" - tensor(x::Ket, y::Ket, z::Ket...) - -Tensor product ``|x⟩⊗|y⟩⊗|z⟩⊗…`` of the given states. -""" -tensor(a::T, b::T) where {T<:StateVector} = T(tensor(a.basis, b.basis), kron(b.data, a.data)) -tensor(state::StateVector) = state -tensor(states::T...) where {T<:StateVector} = reduce(tensor, states) - -# Normalization functions -""" - norm(x::StateVector) - -Norm of the given bra or ket state. -""" -norm(x::StateVector) = norm(x.data) -""" - normalize(x::StateVector) - -Return the normalized state so that `norm(x)` is one. -""" -normalize(x::StateVector) = x/norm(x) -""" - normalize!(x::StateVector) - -In-place normalization of the given bra or ket so that `norm(x)` is one. -""" -normalize!(x::StateVector) = scale!(x.data, 1./norm(x)) - -function permutesystems(state::T, perm::Vector{Int}) where T<:StateVector - @assert length(state.basis.bases) == length(perm) - @assert isperm(perm) - data = reshape(state.data, state.basis.shape...) - data = permutedims(data, perm) - data = reshape(data, length(data)) - T(permutesystems(state.basis, perm), data) -end - -# Creation of basis states. -""" - basisstate(b, index) - -Basis vector specified by `index` as ket state. - -For a composite system `index` can be a vector which then creates a tensor -product state ``|i_1⟩⊗|i_2⟩⊗…⊗|i_n⟩`` of the corresponding basis states. -""" -function basisstate(b::Basis, indices::Vector{Int}) - @assert length(b.shape) == length(indices) - x = zeros(Complex128, length(b)) - x[sub2ind(tuple(b.shape...), indices...)] = Complex(1.) - Ket(b, x) -end - -function basisstate(b::Basis, index::Int) - data = zeros(Complex128, length(b)) - data[index] = Complex(1.) - Ket(b, data) -end - - -# Helper functions to check validity of arguments -function check_multiplicable(a::Bra, b::Ket) - if a.basis != b.basis - throw(IncompatibleBases()) - end -end - -samebases(a::T, b::T) where {T<:StateVector} = samebases(a.basis, b.basis)::Bool - -end # module diff --git a/src/steadystate.jl b/src/steadystate.jl index add72c56..85501ef4 100644 --- a/src/steadystate.jl +++ b/src/steadystate.jl @@ -1,8 +1,8 @@ module steadystate -using ..states, ..operators, ..operators_dense, ..superoperators +using QuantumOpticsBase using ..timeevolution - +using Arpack, LinearAlgebra """ steadystate.master(H, J; ) @@ -26,13 +26,13 @@ Calculate steady state using long time master equation evolution. density operator `rho` is further used and therefore must not be changed. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function master(H::Operator, J::Vector; - rho0::DenseOperator=tensor(basisstate(H.basis_l, 1), dagger(basisstate(H.basis_r, 1))), +function master(H::AbstractOperator{B,B}, J::Vector; + rho0::DenseOperator{B,B}=tensor(basisstate(H.basis_l, 1), dagger(basisstate(H.basis_r, 1))), hmin=1e-7, tol=1e-3, - rates::Union{Vector{Float64}, Matrix{Float64}, Void}=nothing, + rates::Union{Vector{Float64}, Matrix{Float64}, Nothing}=nothing, Jdagger::Vector=dagger.(J), - fout::Union{Function,Void}=nothing, - kwargs...) + fout::Union{Function,Nothing}=nothing, + kwargs...) where B<:Basis t,u = timeevolution.master([0., Inf], rho0, H, J; rates=rates, Jdagger=Jdagger, hmin=hmin, hmax=Inf, display_initialvalue=false, @@ -48,46 +48,47 @@ end steadystate.liouvillianspectrum(H, J) Calculate eigenspectrum of the Liouvillian matrix `L`. The eigenvalues and -states are -sorted according to the real part of the eigenvalues. +sorted according to the absolute value of the eigenvalues. # Keyword arguments: * `nev = min(10, length(L.basis_r[1])*length(L.basis_r[2]))`: Number of eigenvalues. -* `which = :LR`: Find eigenvalues with largest real part. Keyword for `eigs` function (ineffective for DenseSuperOperator). -* `kwargs...`: Keyword arguments for the Julia `eig` or `eigs` function. +* `which = :LR`: Find eigenvalues with largest real part. Keyword for `eigs` + function (ineffective for DenseSuperOperator). +* `kwargs...`: Keyword arguments for the Julia `eigen` or `eigens` function. """ function liouvillianspectrum(L::DenseSuperOperator; nev::Int = min(10, length(L.basis_r[1])*length(L.basis_r[2])), which::Symbol = :LR, kwargs...) - d, v = Base.eig(L.data; kwargs...) - indices = sortperm(-real(d))[1:nev] + d, v = eigen(L.data; kwargs...) + indices = sortperm(abs.(d))[1:nev] ops = DenseOperator[] for i in indices data = reshape(v[:,i], length(L.basis_r[1]), length(L.basis_r[2])) op = DenseOperator(L.basis_r[1], L.basis_r[2], data) - push!(ops, op/trace(op)) + push!(ops, op) end return d[indices], ops end function liouvillianspectrum(L::SparseSuperOperator; nev::Int = min(10, length(L.basis_r[1])*length(L.basis_r[2])), which::Symbol = :LR, kwargs...) d, v, nconv, niter, nmult, resid = try - Base.eigs(L.data; nev = nev, which = which, kwargs...) + eigs(L.data; nev = nev, which = which, kwargs...) catch err - if isa(err, LinAlg.SingularException) || isa(err, LinAlg.ARPACKException) - error("Base.LinAlg.eigs() algorithm failed; try using DenseOperators or change nev.") + if isa(err, SingularException) || isa(err, Arpack.ARPACKException) + error("Arpack's eigs() algorithm failed; try using DenseOperators or change nev.") else rethrow(err) end end - indices = sortperm(-real(d))[1:nev] + indices = sortperm(abs.(d))[1:nev] ops = DenseOperator[] for i in indices data = reshape(v[:,i], length(L.basis_r[1]), length(L.basis_r[2])) op = DenseOperator(L.basis_r[1], L.basis_r[2], data) - push!(ops, op/trace(op)) + push!(ops, op) end return d[indices], ops end -liouvillianspectrum(H::Operator, J::Vector; rates::Union{Vector{Float64}, Matrix{Float64}}=ones(Float64, length(J)), kwargs...) = liouvillianspectrum(liouvillian(H, J; rates=rates); kwargs...) +liouvillianspectrum(H::AbstractOperator{B,B}, J::Vector; rates::Union{Vector{Float64}, Matrix{Float64}}=ones(Float64, length(J)), kwargs...) where B<:Basis = liouvillianspectrum(liouvillian(H, J; rates=rates); kwargs...) """ steadystate.eigenvector(L) @@ -96,30 +97,28 @@ liouvillianspectrum(H::Operator, J::Vector; rates::Union{Vector{Float64}, Matrix Find steady state by calculating the eigenstate with eigenvalue 0 of the Liouvillian matrix `L`, if it exists. # Keyword arguments: -* `tol = 1e-9`: Check `abs(real(eigenvalue)) < tol` to determine zero eigenvalue. +* `tol = 1e-9`: Check `abs(eigenvalue) < tol` to determine zero eigenvalue. * `nev = 2`: Number of calculated eigenvalues. If `nev > 1` it is checked if there -is only one eigenvalue with real part 0. No checks for `nev = 1`: use if faster -or for avoiding convergence errors of `eigs`. Changing `nev` thus only makes sense when using SparseSuperOperator. + is only one eigenvalue with real part 0. No checks for `nev = 1`: use if + faster or for avoiding convergence errors of `eigs`. Changing `nev` thus only + makes sense when using SparseSuperOperator. * `which = :LR`: Find eigenvalues with largest real part. Keyword for `eigs` function (ineffective for DenseSuperOperator). -* `kwargs...`: Keyword arguments for the Julia `eig` or `eigs` function. +* `kwargs...`: Keyword arguments for the Julia `eigen` or `eigs` function. """ function eigenvector(L::SuperOperator; tol::Real = 1e-9, nev::Int = 2, which::Symbol = :LR, kwargs...) d, ops = liouvillianspectrum(L; nev = nev, which = which, kwargs...) - if abs(real(d[1])) > tol - error("Eigenvalue with largest real part is not zero.") + if abs(d[1]) > tol + error("Eigenvalue with smallest absolute value is not zero.") end if nev > 1 if abs(real(d[2])) < tol - warn("Several eigenvalues with real part 0 detected; use steadystate.liouvillianspectrum to find out more.") + @warn("Several eigenvalues with real part 0 detected; use steadystate.liouvillianspectrum to find out more.") end end - if abs(imag(d[1])) > tol - warn("Imaginary part of eigenvalue not zero.") - end - return ops[1] + return ops[1]/tr(ops[1]) end -eigenvector(H::Operator, J::Vector; rates::Union{Vector{Float64}, Matrix{Float64}}=ones(Float64, length(J)), kwargs...) = eigenvector(liouvillian(H, J; rates=rates); kwargs...) +eigenvector(H::AbstractOperator, J::Vector; rates::Union{Vector{Float64}, Matrix{Float64}}=ones(Float64, length(J)), kwargs...) = eigenvector(liouvillian(H, J; rates=rates); kwargs...) end # module diff --git a/src/stochastic_base.jl b/src/stochastic_base.jl new file mode 100644 index 00000000..e8d89779 --- /dev/null +++ b/src/stochastic_base.jl @@ -0,0 +1,94 @@ +using QuantumOpticsBase +using QuantumOpticsBase: check_samebases, check_multiplicable +import ..timeevolution: recast!, QO_CHECKS, DiffArray, pure_inference + +import DiffEqCallbacks, StochasticDiffEq, OrdinaryDiffEq + +""" + integrate_stoch(tspan::Vector{Float64}, df::Function, dg::Vector{Function}, x0::Vector{ComplexF64}, + state::T, dstate::T, fout::Function; kwargs...) + +Integrate using StochasticDiffEq +""" +function integrate_stoch(tspan::Vector{Float64}, df::Function, dg::Function, x0::Vector{ComplexF64}, + state::T, dstate::T, fout::Function, n::Int; + save_everystep = false, callback=nothing, + alg::StochasticDiffEq.StochasticDiffEqAlgorithm=StochasticDiffEq.EM(), + noise_rate_prototype = nothing, + noise_prototype_classical = nothing, + noise=nothing, + ncb=nothing, + kwargs...) where T + + function df_(dx::Vector{ComplexF64}, x::Vector{ComplexF64}, p, t) + recast!(x, state) + recast!(dx, dstate) + df(t, state, dstate) + recast!(dstate, dx) + end + + function dg_(dx::Union{Vector{ComplexF64}, Array{ComplexF64, 2}}, + x::Vector{ComplexF64}, p, t) + recast!(x, state) + dg(dx, t, state, dstate, n) + end + + function fout_(x::Vector{ComplexF64}, t::Float64, integrator) + recast!(x, state) + fout(t, state) + end + + nc = isa(noise_prototype_classical, Nothing) ? 0 : size(noise_prototype_classical)[2] + if isa(noise, Nothing) && n > 0 + if n + nc == 1 + noise_ = StochasticDiffEq.RealWienerProcess(0.0, 0.0) + else + noise_ = StochasticDiffEq.RealWienerProcess!(0.0, zeros(n + nc)) + end + else + noise_ = noise + end + if isa(noise_rate_prototype, Nothing) + if n > 1 || nc > 1 || (n > 0 && nc > 0) + noise_rate_prototype = zeros(ComplexF64, length(x0), n + nc) + end + end + + out_type = pure_inference(fout, Tuple{eltype(tspan),typeof(state)}) + + out = DiffEqCallbacks.SavedValues(Float64,out_type) + + scb = DiffEqCallbacks.SavingCallback(fout_,out,saveat=tspan, + save_everystep=save_everystep, + save_start = false) + + full_cb = OrdinaryDiffEq.CallbackSet(callback, ncb, scb) + + prob = StochasticDiffEq.SDEProblem{true}(df_, dg_, x0,(tspan[1],tspan[end]), + noise=noise_, + noise_rate_prototype=noise_rate_prototype) + + sol = StochasticDiffEq.solve( + prob, + alg; + reltol = 1.0e-3, + abstol = 1.0e-3, + save_everystep = false, save_start = false, + save_end = false, + callback=full_cb, kwargs...) + + out.t,out.saveval +end + +""" + integrate_stoch + +Define fout if it was omitted. +""" +function integrate_stoch(tspan::Vector{Float64}, df::Function, dg::Function, x0::Vector{ComplexF64}, + state::T, dstate::T, ::Nothing, n::Int; kwargs...) where T + function fout(t::Float64, state::T) + copy(state) + end + integrate_stoch(tspan, df, dg, x0, state, dstate, fout, n; kwargs...) +end diff --git a/src/stochastic_definitions.jl b/src/stochastic_definitions.jl index ed4ad63d..b13fe459 100644 --- a/src/stochastic_definitions.jl +++ b/src/stochastic_definitions.jl @@ -1,9 +1,3 @@ -module stochastic_definitions - -export homodyne_carmichael - -using ...operators, ...states - """ stochastic.homodyne_carmichael(H0, C, theta) @@ -44,8 +38,8 @@ and H_s = iCe^{-i\\theta}. ``` """ -function homodyne_carmichael(H0::Operator, C::Vector{T}, theta::Vector{R}; - normalize_expect::Bool=true) where {T <: Operator, R <: Real} +function homodyne_carmichael(H0::AbstractOperator, C::Vector{T}, theta::Vector{R}; + normalize_expect::Bool=true) where {T <: AbstractOperator, R <: Real} n = length(C) @assert n == length(theta) Hs = 1.0im*C .* exp.(-1.0im .* theta) @@ -67,7 +61,5 @@ function homodyne_carmichael(H0::Operator, C::Vector{T}, theta::Vector{R}; return fdeterm_un, fstoch end end -homodyne_carmichael(H0::Operator, C::Operator, theta::Real; kwargs...) = +homodyne_carmichael(H0::AbstractOperator, C::AbstractOperator, theta::Real; kwargs...) = homodyne_carmichael(H0, [C], [theta]; kwargs...) - -end # module diff --git a/src/stochastic_master.jl b/src/stochastic_master.jl index 76b7a646..8fcda8e8 100644 --- a/src/stochastic_master.jl +++ b/src/stochastic_master.jl @@ -1,15 +1,6 @@ -module stochastic_master +import ...timeevolution: dmaster_h, dmaster_nh, dmaster_h_dynamic, check_master -export master, master_dynamic - -using ...bases, ...states, ...operators -using ...operators_dense, ...operators_sparse -using ...timeevolution -import ...timeevolution: integrate_stoch, recast! -import ...timeevolution.timeevolution_master: dmaster_h, dmaster_nh, dmaster_h_dynamic, check_master - -const DecayRates = Union{Vector{Float64}, Matrix{Float64}, Void} -const DiffArray = Union{Vector{Complex128}, Array{Complex128, 2}} +const DecayRates = Union{Vector{Float64}, Matrix{Float64}, Nothing} """ stochastic.master(tspan, rho0, H, J, C; ) @@ -40,23 +31,23 @@ non-hermitian Hamiltonian and then calls master_nh which is slightly faster. be changed. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function master(tspan, rho0::DenseOperator, H::Operator, +function master(tspan, rho0::T, H::AbstractOperator{B,B}, J::Vector, C::Vector; rates::DecayRates=nothing, Jdagger::Vector=dagger.(J), Cdagger::Vector=dagger.(C), - fout::Union{Function,Void}=nothing, - kwargs...) + fout::Union{Function,Nothing}=nothing, + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} tmp = copy(rho0) n = length(C) - dmaster_stoch(dx::DiffArray, t::Float64, rho::DenseOperator, drho::DenseOperator, n::Int) = + dmaster_stoch(dx::DiffArray, t::Float64, rho::T, drho::T, n::Int) = dmaster_stochastic(dx, rho, C, Cdagger, drho, n) - isreducible = check_master(rho0, H, J, Jdagger, rates) + isreducible = check_master(rho0, H, J, Jdagger, rates) && check_master_stoch(rho0, C, Cdagger) if !isreducible - dmaster_h_determ(t::Float64, rho::DenseOperator, drho::DenseOperator) = + dmaster_h_determ(t::Float64, rho::T, drho::T) = dmaster_h(rho, H, rates, J, Jdagger, drho, tmp) integrate_master_stoch(tspan, dmaster_h_determ, dmaster_stoch, rho0, fout, n; kwargs...) else @@ -76,7 +67,7 @@ function master(tspan, rho0::DenseOperator, H::Operator, end Hnhdagger = dagger(Hnh) - dmaster_nh_determ(t::Float64, rho::DenseOperator, drho::DenseOperator) = + dmaster_nh_determ(t::Float64, rho::T, drho::T) = dmaster_nh(rho, Hnh, Hnhdagger, rates, J, Jdagger, drho, tmp) integrate_master_stoch(tspan, dmaster_nh_determ, dmaster_stoch, rho0, fout, n; kwargs...) end @@ -112,11 +103,11 @@ dynamic Hamiltonian and J. number if you want to avoid an initial calculation of function outputs! * `kwargs...`: Further arguments are passed on to the ode solver. """ -function master_dynamic(tspan::Vector{Float64}, rho0::DenseOperator, fdeterm::Function, fstoch::Function; +function master_dynamic(tspan::Vector{Float64}, rho0::T, fdeterm::Function, fstoch::Function; rates::DecayRates=nothing, - fout::Union{Function,Void}=nothing, + fout::Union{Function,Nothing}=nothing, noise_processes::Int=0, - kwargs...) + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} tmp = copy(rho0) @@ -127,43 +118,44 @@ function master_dynamic(tspan::Vector{Float64}, rho0::DenseOperator, fdeterm::Fu n = noise_processes end - dmaster_determ(t::Float64, rho::DenseOperator, drho::DenseOperator) = dmaster_h_dynamic(t, rho, fdeterm, rates, drho, tmp) - dmaster_stoch(dx::DiffArray, t::Float64, rho::DenseOperator, drho::DenseOperator, n::Int) = + dmaster_determ(t::Float64, rho::T, drho::T) = dmaster_h_dynamic(t, rho, fdeterm, rates, drho, tmp) + dmaster_stoch(dx::DiffArray, t::Float64, rho::T, drho::T, n::Int) = dmaster_stoch_dynamic(dx, t, rho, fstoch, drho, n) integrate_master_stoch(tspan, dmaster_determ, dmaster_stoch, rho0, fout, n; kwargs...) end master_dynamic(tspan::Vector{Float64}, psi0::Ket, args...; kwargs...) = master_dynamic(tspan, dm(psi0), args...; kwargs...) # Derivative functions -function dmaster_stochastic(dx::Vector{Complex128}, rho::DenseOperator, - C::Vector, Cdagger::Vector, drho::DenseOperator, ::Int) +function dmaster_stochastic(dx::Vector{ComplexF64}, rho::T, + C::Vector, Cdagger::Vector, drho::T, ::Int) where {B<:Basis,T<:DenseOperator{B,B}} recast!(dx, drho) - operators.gemm!(1, C[1], rho, 0, drho) - operators.gemm!(1, rho, Cdagger[1], 1, drho) - drho.data .-= trace(drho)*rho.data + QuantumOpticsBase.gemm!(1, C[1], rho, 0, drho) + QuantumOpticsBase.gemm!(1, rho, Cdagger[1], 1, drho) + drho.data .-= tr(drho)*rho.data end -function dmaster_stochastic(dx::Array{Complex128, 2}, rho::DenseOperator, - C::Vector, Cdagger::Vector, drho::DenseOperator, n::Int) +function dmaster_stochastic(dx::Array{ComplexF64, 2}, rho::T, + C::Vector, Cdagger::Vector, drho::T, n::Int) where {B<:Basis,T<:DenseOperator{B,B}} for i=1:n dx_i = @view dx[:, i] recast!(dx_i, drho) - operators.gemm!(1, C[i], rho, 0, drho) - operators.gemm!(1, rho, Cdagger[i], 1, drho) - drho.data .-= trace(drho)*rho.data + QuantumOpticsBase.gemm!(1, C[i], rho, 0, drho) + QuantumOpticsBase.gemm!(1, rho, Cdagger[i], 1, drho) + drho.data .-= tr(drho)*rho.data recast!(drho, dx_i) end end -function dmaster_stoch_dynamic(dx::DiffArray, t::Float64, rho::DenseOperator, - f::Function, drho::DenseOperator, n::Int) +function dmaster_stoch_dynamic(dx::DiffArray, t::Float64, rho::T, + f::Function, drho::T, n::Int) where {B<:Basis,T<:DenseOperator{B,B}} result = f(t, rho) - @assert 2 == length(result) + QO_CHECKS[] && @assert 2 == length(result) C, Cdagger = result + QO_CHECKS[] && check_master_stoch(rho, C, Cdagger) dmaster_stochastic(dx, rho, C, Cdagger, drho, n) end function integrate_master_stoch(tspan, df::Function, dg::Function, - rho0::DenseOperator, fout::Union{Void, Function}, + rho0::DenseOperator, fout::Union{Nothing, Function}, n::Int; kwargs...) tspan_ = convert(Vector{Float64}, tspan) @@ -173,11 +165,29 @@ function integrate_master_stoch(tspan, df::Function, dg::Function, integrate_stoch(tspan_, df, dg, x0, state, dstate, fout, n; kwargs...) end +function check_master_stoch(rho0::DenseOperator{B,B}, C::Vector, Cdagger::Vector) where B<:Basis + # TODO: replace type checks by dispatch; make types of C known + @assert length(C) == length(Cdagger) + isreducible = true + for c=C + @assert isa(c, AbstractOperator{B,B}) + if !(isa(c, DenseOperator) || isa(c, SparseOperator)) + isreducible = false + end + end + for c=Cdagger + @assert isa(c, AbstractOperator{B,B}) + if !(isa(c, DenseOperator) || isa(c, SparseOperator)) + isreducible = false + end + end + isreducible +end + + # TODO: Speed up by recasting to n-d arrays, remove vector methods -function recast!(x::Union{Vector{Complex128}, SubArray{Complex128, 1}}, rho::DenseOperator) +function recast!(x::Union{Vector{ComplexF64}, SubArray{ComplexF64, 1}}, rho::DenseOperator{B,B,T}) where {B<:Basis,T<:Matrix{ComplexF64}} rho.data = reshape(x, size(rho.data)) end -recast!(state::DenseOperator, x::SubArray{Complex128, 1}) = (x[:] = state.data) -recast!(state::DenseOperator, x::Vector{Complex128}) = nothing - -end # module +recast!(state::DenseOperator{B,B}, x::SubArray{ComplexF64, 1}) where B<:Basis = (x[:] = state.data) +recast!(state::DenseOperator{B,B}, x::Vector{ComplexF64}) where B<:Basis = nothing diff --git a/src/stochastic_schroedinger.jl b/src/stochastic_schroedinger.jl index ca85dddb..6e114844 100644 --- a/src/stochastic_schroedinger.jl +++ b/src/stochastic_schroedinger.jl @@ -1,16 +1,4 @@ -module stochastic_schroedinger - -export schroedinger, schroedinger_dynamic - -using ...bases, ...states, ...operators -using ...operators_dense, ...operators_sparse -using ...timeevolution -import ...timeevolution: integrate_stoch, recast! -import ...timeevolution.timeevolution_schroedinger: dschroedinger, dschroedinger_dynamic, check_schroedinger - -import DiffEqCallbacks - -const DiffArray = Union{Vector{Complex128}, Array{Complex128, 2}} +import ...timeevolution: dschroedinger, dschroedinger_dynamic, check_schroedinger """ stochastic.schroedinger(tspan, state0, H, Hs[; fout, ...]) @@ -31,11 +19,11 @@ Integrate stochastic Schrödinger equation. each time step taken by the solver. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function schroedinger(tspan, psi0::Ket, H::Operator, Hs::Vector; - fout::Union{Function,Void}=nothing, +function schroedinger(tspan, psi0::T, H::AbstractOperator{B,B}, Hs::Vector; + fout::Union{Function,Nothing}=nothing, normalize_state::Bool=false, calback=nothing, - kwargs...) + kwargs...) where {B<:Basis,T<:Ket{B}} tspan_ = convert(Vector{Float64}, tspan) n = length(Hs) @@ -43,13 +31,17 @@ function schroedinger(tspan, psi0::Ket, H::Operator, Hs::Vector; x0 = psi0.data state = copy(psi0) - check_schroedinger(psi0, H) - dschroedinger_determ(t::Float64, psi::Ket, dpsi::Ket) = dschroedinger(psi, H, dpsi) + # TODO: replace checks by dispatch + for h=Hs + @assert isa(h, AbstractOperator{B,B}) + end + + dschroedinger_determ(t::Float64, psi::T, dpsi::T) = dschroedinger(psi, H, dpsi) dschroedinger_stoch(dx::DiffArray, - t::Float64, psi::Ket, dpsi::Ket, n::Int) = dschroedinger_stochastic(dx, psi, Hs, dpsi, n) + t::Float64, psi::T, dpsi::T, n::Int) = dschroedinger_stochastic(dx, psi, Hs, dpsi, n) if normalize_state - norm_func(u::Vector{Complex128}, t::Float64, integrator) = normalize!(u) + norm_func(u::Vector{ComplexF64}, t::Float64, integrator) = normalize!(u) ncb = DiffEqCallbacks.FunctionCallingCallback(norm_func; func_everystep=true, func_start=false) @@ -61,7 +53,7 @@ function schroedinger(tspan, psi0::Ket, H::Operator, Hs::Vector; ncb=ncb, kwargs...) end -schroedinger(tspan, psi0::Ket, H::Operator, Hs::Operator; kwargs...) = schroedinger(tspan, psi0, H, [Hs]; kwargs...) +schroedinger(tspan, psi0::Ket{B}, H::AbstractOperator{B,B}, Hs::AbstractOperator{B,B}; kwargs...) where B<:Basis = schroedinger(tspan, psi0, H, [Hs]; kwargs...) """ stochastic.schroedinger_dynamic(tspan, state0, fdeterm, fstoch[; fout, ...]) @@ -89,10 +81,10 @@ Integrate stochastic Schrödinger equation with dynamic Hamiltonian. each time step taken by the solver. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function schroedinger_dynamic(tspan, psi0::Ket, fdeterm::Function, fstoch::Function; - fout::Union{Function,Void}=nothing, noise_processes::Int=0, +function schroedinger_dynamic(tspan, psi0::T, fdeterm::Function, fstoch::Function; + fout::Union{Function,Nothing}=nothing, noise_processes::Int=0, normalize_state::Bool=false, - kwargs...) + kwargs...) where T<:Ket tspan_ = convert(Vector{Float64}, tspan) if noise_processes == 0 @@ -106,13 +98,13 @@ function schroedinger_dynamic(tspan, psi0::Ket, fdeterm::Function, fstoch::Funct x0 = psi0.data state = copy(psi0) - dschroedinger_determ(t::Float64, psi::Ket, dpsi::Ket) = dschroedinger_dynamic(t, psi, fdeterm, dpsi) + dschroedinger_determ(t::Float64, psi::T, dpsi::T) = dschroedinger_dynamic(t, psi, fdeterm, dpsi) dschroedinger_stoch(dx::DiffArray, - t::Float64, psi::Ket, dpsi::Ket, n::Int) = + t::Float64, psi::T, dpsi::T, n::Int) = dschroedinger_stochastic(dx, t, psi, fstoch, dpsi, n) if normalize_state - norm_func(u::Vector{Complex128}, t::Float64, integrator) = normalize!(u) + norm_func(u::Vector{ComplexF64}, t::Float64, integrator) = normalize!(u) ncb = DiffEqCallbacks.FunctionCallingCallback(norm_func; func_everystep=true, func_start=false) @@ -127,16 +119,14 @@ function schroedinger_dynamic(tspan, psi0::Ket, fdeterm::Function, fstoch::Funct end -function dschroedinger_stochastic(dx::Vector{Complex128}, psi::Ket, Hs::Vector{T}, - dpsi::Ket, index::Int) where T <: Operator - check_schroedinger(psi, Hs[index]) +function dschroedinger_stochastic(dx::D, psi::T1, Hs::Vector{T2}, + dpsi::T1, index::Int) where {D<:Vector{ComplexF64},B<:Basis,T1<:Ket{B},T2<:AbstractOperator{B,B}} recast!(dx, dpsi) dschroedinger(psi, Hs[index], dpsi) end -function dschroedinger_stochastic(dx::Array{Complex128, 2}, psi::Ket, Hs::Vector{T}, - dpsi::Ket, n::Int) where T <: Operator +function dschroedinger_stochastic(dx::Array{ComplexF64, 2}, psi::T1, Hs::Vector{T2}, + dpsi::T1, n::Int) where {B<:Basis,T1<:Ket{B},T2<:AbstractOperator{B,B}} for i=1:n - check_schroedinger(psi, Hs[i]) dx_i = @view dx[:, i] recast!(dx_i, dpsi) dschroedinger(psi, Hs[i], dpsi) @@ -144,12 +134,15 @@ function dschroedinger_stochastic(dx::Array{Complex128, 2}, psi::Ket, Hs::Vector end end function dschroedinger_stochastic(dx::DiffArray, - t::Float64, psi::Ket, f::Function, dpsi::Ket, n::Int) + t::Float64, psi::T, f::Function, dpsi::T, n::Int) where T<:Ket ops = f(t, psi) + if QO_CHECKS[] + @inbounds for h=ops + check_schroedinger(psi, h) + end + end dschroedinger_stochastic(dx, psi, ops, dpsi, n) end -recast!(psi::StateVector, x::SubArray{Complex128, 1}) = (x .= psi.data) -recast!(x::SubArray{Complex128, 1}, psi::StateVector) = (psi.data = x) - -end # module +recast!(psi::Ket, x::SubArray{ComplexF64, 1}) = (x .= psi.data) +recast!(x::SubArray{ComplexF64, 1}, psi::Ket) = (psi.data = x) diff --git a/src/stochastic_semiclassical.jl b/src/stochastic_semiclassical.jl index cabf97de..935a1640 100644 --- a/src/stochastic_semiclassical.jl +++ b/src/stochastic_semiclassical.jl @@ -1,26 +1,9 @@ -module stochastic_semiclassical - -export schroedinger_semiclassical, master_semiclassical - -using ...bases, ...states, ...operators -using ...operators_dense, ...operators_sparse using ...semiclassical -import ...semiclassical: recast!, State, dmaster_h_dynamic -using ...timeevolution -import ...timeevolution: integrate_stoch -import ...timeevolution.timeevolution_schroedinger: dschroedinger, dschroedinger_dynamic -using ...stochastic - -import DiffEqCallbacks - -const DecayRates = Union{Vector{Float64}, Matrix{Float64}, Void} -const DiffArray = Union{Vector{Complex128}, Array{Complex128, 2}} +import ...semiclassical: State """ - semiclassical.schroedinger_stochastic(tspan, state0, fquantum, fclassical[; fout, ...]) - + stochastic.schroedinger_semiclassical(tspan, state0, fquantum, fclassical[; fout, ...]) Integrate time-dependent Schrödinger equation coupled to a classical system. - # Arguments * `tspan`: Vector specifying the points of time for which the output should be displayed. @@ -54,22 +37,23 @@ Integrate time-dependent Schrödinger equation coupled to a classical system. each time step taken by the solver. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function schroedinger_semiclassical(tspan, state0::State{Ket}, fquantum::Function, - fclassical::Function; fstoch_quantum::Union{Void, Function}=nothing, - fstoch_classical::Union{Void, Function}=nothing, - fout::Union{Function,Void}=nothing, +function schroedinger_semiclassical(tspan, state0::S, fquantum::Function, + fclassical::Function; fstoch_quantum::Union{Nothing, Function}=nothing, + fstoch_classical::Union{Nothing, Function}=nothing, + fout::Union{Function,Nothing}=nothing, noise_processes::Int=0, noise_prototype_classical=nothing, normalize_state::Bool=false, - kwargs...) + kwargs...) where {B<:Basis,T<:Ket{B},S<:State{B,T}} tspan_ = convert(Vector{Float64}, tspan) - dschroedinger_det(t::Float64, state::State{Ket}, dstate::State{Ket}) = semiclassical.dschroedinger_dynamic(t, state, fquantum, fclassical, dstate) + dschroedinger_det(t::Float64, state::S, dstate::S) = + semiclassical.dschroedinger_dynamic(t, state, fquantum, fclassical, dstate) - if isa(fstoch_quantum, Void) && isa(fstoch_classical, Void) + if isa(fstoch_quantum, Nothing) && isa(fstoch_classical, Nothing) throw(ArgumentError("No stochastic functions provided!")) end - x0 = Vector{Complex128}(length(state0)) + x0 = Vector{ComplexF64}(undef, length(state0)) recast!(state0, x0) state = copy(state0) dstate = copy(state0) @@ -85,15 +69,15 @@ function schroedinger_semiclassical(tspan, state0::State{Ket}, fquantum::Functio end if n > 0 && isa(fstoch_classical, Function) - if isa(noise_prototype_classical, Void) + if isa(noise_prototype_classical, Nothing) throw(ArgumentError("noise_prototype_classical must be set for combinations of quantum and classical noise!")) end end if normalize_state len_q = length(state0.quantum) - function norm_func(u::Vector{Complex128}, t::Float64, integrator) - u .= [normalize!(u[1:len_q]), u[len_q+1:end];] + function norm_func(u::Vector{ComplexF64}, t::Float64, integrator) + u .= [normalize!(u[1:len_q]); u[len_q+1:end]] end ncb = DiffEqCallbacks.FunctionCallingCallback(norm_func; func_everystep=true, @@ -102,9 +86,9 @@ function schroedinger_semiclassical(tspan, state0::State{Ket}, fquantum::Functio ncb = nothing end - dschroedinger_stoch(dx::DiffArray, - t::Float64, state::State{Ket}, dstate::State{Ket}, n::Int) = + dschroedinger_stoch(dx::DiffArray, t::Float64, state::S, dstate::S, n::Int) = dschroedinger_stochastic(dx, t, state, fstoch_quantum, fstoch_classical, dstate, n) + integrate_stoch(tspan_, dschroedinger_det, dschroedinger_stoch, x0, state, dstate, fout, n; noise_prototype_classical = noise_prototype_classical, ncb=ncb, @@ -113,12 +97,9 @@ end """ stochastic.master_semiclassical(tspan, rho0, H, Hs, J; ) - Time-evolution according to a stochastic master equation. - For dense arguments the `master` function calculates the non-hermitian Hamiltonian and then calls master_nh which is slightly faster. - # Arguments * `tspan`: Vector specifying the points of time for which output should be displayed. @@ -154,19 +135,19 @@ non-hermitian Hamiltonian and then calls master_nh which is slightly faster. noise. See the documentation for details. * `kwargs...`: Further arguments are passed on to the ode solver. """ -function master_semiclassical(tspan::Vector{Float64}, rho0::State{DenseOperator}, +function master_semiclassical(tspan::Vector{Float64}, rho0::S, fquantum::Function, fclassical::Function; - fstoch_quantum::Union{Function, Void}=nothing, - fstoch_classical::Union{Function, Void}=nothing, + fstoch_quantum::Union{Function, Nothing}=nothing, + fstoch_classical::Union{Function, Nothing}=nothing, rates::DecayRates=nothing, - fout::Union{Function,Void}=nothing, + fout::Union{Function,Nothing}=nothing, noise_processes::Int=0, noise_prototype_classical=nothing, nonlinear::Bool=true, - kwargs...) + kwargs...) where {B<:Basis,T<:DenseOperator{B,B},S<:State{B,T}} tmp = copy(rho0.quantum) - if isa(fstoch_quantum, Void) && isa(fstoch_classical, Void) + if isa(fstoch_quantum, Nothing) && isa(fstoch_classical, Nothing) throw(ArgumentError("No stochastic functions provided!")) end @@ -181,95 +162,99 @@ function master_semiclassical(tspan::Vector{Float64}, rho0::State{DenseOperator} end if n > 0 && isa(fstoch_classical, Function) - if isa(noise_prototype_classical, Void) + if isa(noise_prototype_classical, Nothing) throw(ArgumentError("noise_prototype_classical must be set for combinations of quantum and classical noise!")) end end - dmaster_determ(t::Float64, rho::State{DenseOperator}, drho::State{DenseOperator}) = - dmaster_h_dynamic(t, rho, fquantum, fclassical, rates, drho, tmp) + dmaster_determ(t::Float64, rho::S, drho::S) = + semiclassical.dmaster_h_dynamic(t, rho, fquantum, fclassical, rates, drho, tmp) - dmaster_stoch(dx::DiffArray, t::Float64, rho::State{DenseOperator}, - drho::State{DenseOperator}, n::Int) = + dmaster_stoch(dx::DiffArray, t::Float64, rho::S, + drho::S, n::Int) = dmaster_stoch_dynamic(dx, t, rho, fstoch_quantum, fstoch_classical, drho, n) integrate_master_stoch(tspan, dmaster_determ, dmaster_stoch, rho0, fout, n; noise_prototype_classical=noise_prototype_classical, kwargs...) end -master_semiclassical(tspan::Vector{Float64}, psi0::State{Ket}, args...; kwargs...) = +master_semiclassical(tspan::Vector{Float64}, psi0::State{B,T}, args...; kwargs...) where {B<:Basis,T<:Ket{B}} = master_semiclassical(tspan, dm(psi0), args...; kwargs...) # Derivative functions -function dschroedinger_stochastic(dx::Vector{Complex128}, t::Float64, - state::State{Ket}, fstoch_quantum::Function, fstoch_classical::Void, - dstate::State{Ket}, ::Int) +function dschroedinger_stochastic(dx::Vector{ComplexF64}, t::Float64, + state::S, fstoch_quantum::Function, fstoch_classical::Nothing, + dstate::S, ::Int) where {B<:Basis,T<:Ket{B},S<:State{B,T}} H = fstoch_quantum(t, state.quantum, state.classical) recast!(dx, dstate) + QO_CHECKS[] && check_schroedinger(state.quantum, H[1]) dschroedinger(state.quantum, H[1], dstate.quantum) recast!(dstate, dx) end -function dschroedinger_stochastic(dx::Array{Complex128, 2}, - t::Float64, state::State{Ket}, fstoch_quantum::Function, - fstoch_classical::Void, dstate::State{Ket}, n::Int) +function dschroedinger_stochastic(dx::Array{ComplexF64, 2}, + t::Float64, state::S, fstoch_quantum::Function, + fstoch_classical::Nothing, dstate::S, n::Int) where {B<:Basis,T<:Ket{B},S<:State{B,T}} H = fstoch_quantum(t, state.quantum, state.classical) for i=1:n dx_i = @view dx[:, i] recast!(dx_i, dstate) + QO_CHECKS[] && check_schroedinger(state.quantum, H[i]) dschroedinger(state.quantum, H[i], dstate.quantum) recast!(dstate, dx_i) end end function dschroedinger_stochastic(dx::DiffArray, t::Float64, - state::State{Ket}, fstoch_quantum::Void, fstoch_classical::Function, - dstate::State{Ket}, ::Int) + state::S, fstoch_quantum::Nothing, fstoch_classical::Function, + dstate::S, ::Int) where {B<:Basis,T<:Ket{B},S<:State{B,T}} dclassical = @view dx[length(state.quantum)+1:end, :] fstoch_classical(t, state.quantum, state.classical, dclassical) end -function dschroedinger_stochastic(dx::Array{Complex128, 2}, t::Float64, state::State{Ket}, fstoch_quantum::Function, - fstoch_classical::Function, dstate::State{Ket}, n::Int) +function dschroedinger_stochastic(dx::Array{ComplexF64, 2}, t::Float64, state::S, fstoch_quantum::Function, + fstoch_classical::Function, dstate::S, n::Int) where {B<:Basis,T<:Ket{B},S<:State{B,T}} dschroedinger_stochastic(dx, t, state, fstoch_quantum, nothing, dstate, n) dx_i = @view dx[length(state.quantum)+1:end, n+1:end] fstoch_classical(t, state.quantum, state.classical, dx_i) end -function dmaster_stoch_dynamic(dx::Vector{Complex128}, t::Float64, - state::State{DenseOperator}, fstoch_quantum::Function, - fstoch_classical::Void, dstate::State{DenseOperator}, ::Int) +function dmaster_stoch_dynamic(dx::Vector{ComplexF64}, t::Float64, + state::S, fstoch_quantum::Function, + fstoch_classical::Nothing, dstate::S, ::Int) where {B<:Basis,T<:DenseOperator{B,B},S<:State{B,T}} result = fstoch_quantum(t, state.quantum, state.classical) - @assert length(result) == 2 + QO_CHECKS[] && @assert length(result) == 2 C, Cdagger = result + QO_CHECKS[] && check_master_stoch(state.quantum, C, Cdagger) recast!(dx, dstate) - operators.gemm!(1, C[1], state.quantum, 0, dstate.quantum) - operators.gemm!(1, state.quantum, Cdagger[1], 1, dstate.quantum) - dstate.quantum.data .-= trace(dstate.quantum)*state.quantum.data + QuantumOpticsBase.gemm!(1, C[1], state.quantum, 0, dstate.quantum) + QuantumOpticsBase.gemm!(1, state.quantum, Cdagger[1], 1, dstate.quantum) + dstate.quantum.data .-= tr(dstate.quantum)*state.quantum.data recast!(dstate, dx) end -function dmaster_stoch_dynamic(dx::Array{Complex128, 2}, t::Float64, - state::State{DenseOperator}, fstoch_quantum::Function, - fstoch_classical::Void, dstate::State{DenseOperator}, n::Int) +function dmaster_stoch_dynamic(dx::Array{ComplexF64, 2}, t::Float64, + state::S, fstoch_quantum::Function, + fstoch_classical::Nothing, dstate::S, n::Int) where {B<:Basis,T<:DenseOperator{B,B},S<:State{B,T}} result = fstoch_quantum(t, state.quantum, state.classical) - @assert length(result) == 2 + QO_CHECKS[] && @assert length(result) == 2 C, Cdagger = result + QO_CHECKS[] && check_master_stoch(state.quantum, C, Cdagger) for i=1:n dx_i = @view dx[:, i] recast!(dx_i, dstate) - operators.gemm!(1, C[i], state.quantum, 0, dstate.quantum) - operators.gemm!(1, state.quantum, Cdagger[i], 1, dstate.quantum) - dstate.quantum.data .-= trace(dstate.quantum)*state.quantum.data + QuantumOpticsBase.gemm!(1, C[i], state.quantum, 0, dstate.quantum) + QuantumOpticsBase.gemm!(1, state.quantum, Cdagger[i], 1, dstate.quantum) + dstate.quantum.data .-= tr(dstate.quantum)*state.quantum.data recast!(dstate, dx_i) end end function dmaster_stoch_dynamic(dx::DiffArray, t::Float64, - state::State{DenseOperator}, fstoch_quantum::Void, - fstoch_classical::Function, dstate::State{DenseOperator}, ::Int) + state::S, fstoch_quantum::Nothing, + fstoch_classical::Function, dstate::S, ::Int) where {B<:Basis,T<:DenseOperator{B,B},S<:State{B,T}} dclassical = @view dx[length(state.quantum)+1:end, :] fstoch_classical(t, state.quantum, state.classical, dclassical) end -function dmaster_stoch_dynamic(dx::Array{Complex128, 2}, t::Float64, - state::State{DenseOperator}, fstoch_quantum::Function, - fstoch_classical::Function, dstate::State{DenseOperator}, n::Int) +function dmaster_stoch_dynamic(dx::Array{ComplexF64, 2}, t::Float64, + state::S, fstoch_quantum::Function, + fstoch_classical::Function, dstate::S, n::Int) where {B<:Basis,T<:DenseOperator{B,B},S<:State{B,T}} dmaster_stoch_dynamic(dx, t, state, fstoch_quantum, nothing, dstate, n) dx_i = @view dx[length(state.quantum)+1:end, n+1:end] @@ -277,27 +262,25 @@ function dmaster_stoch_dynamic(dx::Array{Complex128, 2}, t::Float64, end function integrate_master_stoch(tspan, df::Function, dg::Function, - rho0::State{DenseOperator}, fout::Union{Void, Function}, + rho0::State{B,T}, fout::Union{Nothing, Function}, n::Int; - kwargs...) + kwargs...) where {B<:Basis,T<:DenseOperator{B,B}} tspan_ = convert(Vector{Float64}, tspan) - x0 = Vector{Complex128}(length(rho0)) + x0 = Vector{ComplexF64}(undef, length(rho0)) recast!(rho0, x0) state = copy(rho0) dstate = copy(rho0) integrate_stoch(tspan_, df, dg, x0, state, dstate, fout, n; kwargs...) end -function recast!(state::State, x::SubArray{Complex128, 1}) +function recast!(state::State, x::SubArray{ComplexF64, 1}) N = length(state.quantum) - copy!(x, 1, state.quantum.data, 1, N) - copy!(x, N+1, state.classical, 1, length(state.classical)) + copyto!(x, 1, state.quantum.data, 1, N) + copyto!(x, N+1, state.classical, 1, length(state.classical)) x end -function recast!(x::SubArray{Complex128, 1}, state::State) +function recast!(x::SubArray{ComplexF64, 1}, state::State) N = length(state.quantum) - copy!(state.quantum.data, 1, x, 1, N) - copy!(state.classical, 1, x, N+1, length(state.classical)) + copyto!(state.quantum.data, 1, x, 1, N) + copyto!(state.classical, 1, x, N+1, length(state.classical)) end - -end # module diff --git a/src/subspace.jl b/src/subspace.jl deleted file mode 100644 index 503c9ede..00000000 --- a/src/subspace.jl +++ /dev/null @@ -1,99 +0,0 @@ -module subspace - -export SubspaceBasis, projector - -import Base.== -import ..operators_dense: projector - -using ..bases, ..states, ..operators, ..operators_dense - - -""" - SubspaceBasis(basisstates) - -A basis describing a subspace embedded a higher dimensional Hilbert space. -""" -mutable struct SubspaceBasis <: Basis - shape::Vector{Int} - superbasis::Basis - basisstates::Vector{Ket} - basisstates_hash::UInt - - function SubspaceBasis(superbasis::Basis, basisstates::Vector{Ket}) - for state = basisstates - if state.basis != superbasis - throw(ArgumentError("The basis of the basisstates has to be the superbasis.")) - end - end - basisstates_hash = hash([hash(x.data) for x=basisstates]) - new(Int[length(basisstates)], superbasis, basisstates, basisstates_hash) - end -end - -SubspaceBasis(basisstates::Vector{Ket}) = SubspaceBasis(basisstates[1].basis, basisstates) - -==(b1::SubspaceBasis, b2::SubspaceBasis) = b1.superbasis==b2.superbasis && b1.basisstates_hash==b2.basisstates_hash - - -proj(u::Ket, v::Ket) = dagger(v)*u/(dagger(u)*u)*u - -""" - orthonormalize(b::SubspaceBasis) - -Orthonormalize the basis states of the given [`SubspaceBasis`](@ref) - -A modified Gram-Schmidt process is used. -""" -function orthonormalize(b::SubspaceBasis) - V = b.basisstates - U = Ket[] - for (k, v)=enumerate(V) - u = copy(v) - for i=1:k-1 - u -= proj(U[i], u) - end - normalize!(u) - push!(U, u) - end - return SubspaceBasis(U) -end - - -""" - projector(b1, b2) - -Projection operator between subspaces and superspaces or between two subspaces. -""" -function projector(b1::SubspaceBasis, b2::SubspaceBasis) - if b1.superbasis != b2.superbasis - throw(ArgumentError("Both subspace bases have to have the same superbasis.")) - end - T1 = projector(b1, b1.superbasis) - T2 = projector(b2.superbasis, b2) - return T1*T2 -end - -function projector(b1::SubspaceBasis, b2::Basis) - if b1.superbasis != b2 - throw(ArgumentError("Second basis has to be the superbasis of the first one.")) - end - data = zeros(Complex128, length(b1), length(b2)) - for (i, state) = enumerate(b1.basisstates) - data[i,:] = state.data - end - return DenseOperator(b1, b2, data) -end - -function projector(b1::Basis, b2::SubspaceBasis) - if b1 != b2.superbasis - throw(ArgumentError("First basis has to be the superbasis of the second one.")) - end - data = zeros(Complex128, length(b1), length(b2)) - for (i, state) = enumerate(b2.basisstates) - data[:,i] = state.data - end - return DenseOperator(b1, b2, data) -end - - -end # module diff --git a/src/superoperators.jl b/src/superoperators.jl deleted file mode 100644 index de31130d..00000000 --- a/src/superoperators.jl +++ /dev/null @@ -1,221 +0,0 @@ -module superoperators - -export SuperOperator, DenseSuperOperator, SparseSuperOperator, - spre, spost, liouvillian, expm - -import Base: ==, *, /, +, - -import ..bases - -using Compat -using ..bases, ..operators, ..operators_dense, ..operators_sparse - - -""" -Base class for all super operator classes. - -Super operators are bijective mappings from operators given in one specific -basis to operators, possibly given in respect to another, different basis. -To embed super operators in an algebraic framework they are defined with a -left hand basis `basis_l` and a right hand basis `basis_r` where each of -them again consists of a left and right hand basis. -```math -A_{bl_1,bl_2} = S_{(bl_1,bl_2) ↔ (br_1,br_2)} B_{br_1,br_2} -\\\\ -A_{br_1,br_2} = B_{bl_1,bl_2} S_{(bl_1,bl_2) ↔ (br_1,br_2)} -``` -""" -abstract type SuperOperator end - -""" - DenseSuperOperator(b1[, b2, data]) - -SuperOperator stored as dense matrix. -""" -mutable struct DenseSuperOperator <: SuperOperator - basis_l::Tuple{Basis, Basis} - basis_r::Tuple{Basis, Basis} - data::Matrix{Complex128} - function DenseSuperOperator(basis_l::Tuple{Basis, Basis}, basis_r::Tuple{Basis, Basis}, data::Matrix{Complex128}) - if length(basis_l[1])*length(basis_l[2]) != size(data, 1) || - length(basis_r[1])*length(basis_r[2]) != size(data, 2) - throw(DimensionMismatch()) - end - new(basis_l, basis_r, data) - end -end - -function DenseSuperOperator(basis_l::Tuple{Basis, Basis}, basis_r::Tuple{Basis, Basis}) - Nl = length(basis_l[1])*length(basis_l[2]) - Nr = length(basis_r[1])*length(basis_r[2]) - data = zeros(Complex128, Nl, Nr) - DenseSuperOperator(basis_l, basis_r, data) -end - - -""" - SparseSuperOperator(b1[, b2, data]) - -SuperOperator stored as sparse matrix. -""" -mutable struct SparseSuperOperator <: SuperOperator - basis_l::Tuple{Basis, Basis} - basis_r::Tuple{Basis, Basis} - data::SparseMatrixCSC{Complex128, Int} - function SparseSuperOperator(basis_l::Tuple{Basis, Basis}, basis_r::Tuple{Basis, Basis}, data::SparseMatrixCSC{Complex128, Int}) - if length(basis_l[1])*length(basis_l[2]) != size(data, 1) || - length(basis_r[1])*length(basis_r[2]) != size(data, 2) - throw(DimensionMismatch()) - end - new(basis_l, basis_r, data) - end -end - -function SparseSuperOperator(basis_l::Tuple{Basis, Basis}, basis_r::Tuple{Basis, Basis}) - Nl = length(basis_l[1])*length(basis_l[2]) - Nr = length(basis_r[1])*length(basis_r[2]) - data = spzeros(Complex128, Nl, Nr) - SparseSuperOperator(basis_l, basis_r, data) -end - -SuperOperator(basis_l, basis_r, data::SparseMatrixCSC{Complex128, Int}) = SparseSuperOperator(basis_l, basis_r, data) -SuperOperator(basis_l, basis_r, data::Matrix{Complex128}) = DenseSuperOperator(basis_l, basis_r, data) - - -Base.copy(a::T) where {T<:SuperOperator} = T(a.basis_l, a.basis_r, copy(a.data)) - -Base.full(a::SparseSuperOperator) = DenseSuperOperator(a.basis_l, a.basis_r, full(a.data)) -Base.full(a::DenseSuperOperator) = copy(a) - -Base.sparse(a::DenseSuperOperator) = SparseSuperOperator(a.basis_l, a.basis_r, sparse(a.data)) -Base.sparse(a::SparseSuperOperator) = copy(a) - -==(a::T, b::T) where {T<:SuperOperator} = samebases(a, b) && (a.data == b.data) - -Base.length(a::SuperOperator) = length(a.basis_l[1])*length(a.basis_l[2])*length(a.basis_r[1])*length(a.basis_r[2]) -bases.samebases(a::SuperOperator, b::SuperOperator) = samebases(a.basis_l[1], b.basis_l[1]) && samebases(a.basis_l[2], b.basis_l[2]) && - samebases(a.basis_r[1], b.basis_r[1]) && samebases(a.basis_r[2], b.basis_r[2]) -bases.multiplicable(a::SuperOperator, b::SuperOperator) = multiplicable(a.basis_r[1], b.basis_l[1]) && multiplicable(a.basis_r[2], b.basis_l[2]) -bases.multiplicable(a::SuperOperator, b::Operator) = multiplicable(a.basis_r[1], b.basis_l) && multiplicable(a.basis_r[2], b.basis_r) - - -# Arithmetic operations -function *(a::SuperOperator, b::DenseOperator) - check_multiplicable(a, b) - data = a.data*reshape(b.data, length(b.data)) - return DenseOperator(a.basis_l[1], a.basis_l[2], reshape(data, length(a.basis_l[1]), length(a.basis_l[2]))) -end -function *(a::SuperOperator, b::SparseOperator) - check_multiplicable(a, b) - data = a.data*reshape(b.data, length(b.data)) - return SparseOperator(a.basis_l[1], a.basis_l[2], reshape(data, length(a.basis_l[1]), length(a.basis_l[2]))) -end - -function *(a::SuperOperator, b::SuperOperator) - check_multiplicable(a, b) - return SuperOperator(a.basis_l, b.basis_r, a.data*b.data) -end - -*(a::SuperOperator, b::Number) = SuperOperator(a.basis_l, a.basis_r, a.data*b) -*(a::Number, b::SuperOperator) = SuperOperator(b.basis_l, b.basis_r, a*b.data) - -/(a::SuperOperator, b::Number) = SuperOperator(a.basis_l, a.basis_r, a.data/b) - -+(a::SuperOperator, b::SuperOperator) = (check_samebases(a, b); SuperOperator(a.basis_l, a.basis_r, a.data+b.data)) - --(a::SuperOperator) = SuperOperator(a.basis_l, a.basis_r, -a.data) --(a::SuperOperator, b::SuperOperator) = (check_samebases(a, b); SuperOperator(a.basis_l, a.basis_r, a.data-b.data)) - -""" - spre(op) - -Create a super-operator equivalent for right side operator multiplication. - -For operators ``A``, ``B`` the relation - -```math - \\mathrm{spre}(A) B = A B -``` - -holds. `op` can be a dense or a sparse operator. -""" -spre(op::Operator) = SuperOperator((op.basis_l, op.basis_l), (op.basis_r, op.basis_r), tensor(op, identityoperator(op)).data) - -""" -Create a super-operator equivalent for left side operator multiplication. - -For operators ``A``, ``B`` the relation - -```math - \\mathrm{spost}(A) B = B A -``` - -holds. `op` can be a dense or a sparse operator. -""" -spost(op::Operator) = SuperOperator((op.basis_r, op.basis_r), (op.basis_l, op.basis_l), kron(transpose(op.data), identityoperator(op).data)) - - -function _check_input(H::Operator, J::Vector, Jdagger::Vector, rates::Union{Vector{Float64}, Matrix{Float64}}) - for j=J - @assert typeof(j) <: Operator - check_samebases(H, j) - end - for j=Jdagger - @assert typeof(j) <: Operator - check_samebases(H, j) - end - @assert length(J)==length(Jdagger) - if typeof(rates) == Matrix{Float64} - @assert size(rates, 1) == size(rates, 2) == length(J) - elseif typeof(rates) == Vector{Float64} - @assert length(rates) == length(J) - end -end - - -""" - liouvillian(H, J; rates, Jdagger) - -Create a super-operator equivalent to the master equation so that ``\\dot ρ = S ρ``. - -The super-operator ``S`` is defined by - -```math -S ρ = -\\frac{i}{ħ} [H, ρ] + \\sum_i J_i ρ J_i^† - \\frac{1}{2} J_i^† J_i ρ - \\frac{1}{2} ρ J_i^† J_i -``` - -# Arguments -* `H`: Hamiltonian. -* `J`: Vector containing the jump operators. -* `rates`: Vector or matrix specifying the coefficients for the jump operators. -* `Jdagger`: Vector containing the hermitian conjugates of the jump operators. If they - are not given they are calculated automatically. -""" -function liouvillian(H::T, J::Vector{T}; - rates::Union{Vector{Float64}, Matrix{Float64}}=ones(Float64, length(J)), - Jdagger::Vector{T}=dagger.(J)) where T<:Operator - _check_input(H, J, Jdagger, rates) - L = spre(-1im*H) + spost(1im*H) - if typeof(rates) == Matrix{Float64} - for i=1:length(J), j=1:length(J) - jdagger_j = rates[i,j]/2*Jdagger[j]*J[i] - L -= spre(jdagger_j) + spost(jdagger_j) - L += spre(rates[i,j]*J[i]) * spost(Jdagger[j]) - end - elseif typeof(rates) == Vector{Float64} - for i=1:length(J) - jdagger_j = rates[i]/2*Jdagger[i]*J[i] - L -= spre(jdagger_j) + spost(jdagger_j) - L += spre(rates[i]*J[i]) * spost(Jdagger[i]) - end - end - return L -end - -""" - expm(op::DenseSuperOperator) - -Operator exponential which can for example used to calculate time evolutions. -""" -Base.expm(op::DenseSuperOperator) = DenseSuperOperator(op.basis_l, op.basis_r, expm(op.data)) - -end # module diff --git a/src/timecorrelations.jl b/src/timecorrelations.jl index cedabb23..a9b4867b 100644 --- a/src/timecorrelations.jl +++ b/src/timecorrelations.jl @@ -2,8 +2,10 @@ module timecorrelations export correlation, spectrum, correlation2spectrum -using ..states, ..operators, ..operators_dense -using ..metrics, ..timeevolution, ..steadystate +using QuantumOpticsBase +using ..timeevolution, ..steadystate + +using FFTW """ @@ -31,11 +33,11 @@ criterion specified in [`steadystate.master`](@ref). * `Jdagger=dagger.(J)`: Vector containing the hermitian conjugates of the jump * `kwargs...`: Further arguments are passed on to the ode solver. """ -function correlation(tspan::Vector{Float64}, rho0::DenseOperator, H::Operator, J::Vector, - op1::Operator, op2::Operator; - rates::Union{Vector{Float64}, Matrix{Float64}, Void}=nothing, +function correlation(tspan::Vector{Float64}, rho0::DenseOperator{B,B}, H::AbstractOperator{B,B}, J::Vector, + op1::AbstractOperator{B,B}, op2::AbstractOperator{B,B}; + rates::Union{Vector{Float64}, Matrix{Float64}, Nothing}=nothing, Jdagger::Vector=dagger.(J), - kwargs...) + kwargs...) where B<:Basis function fout(t, rho) expect(op1, rho) end @@ -44,12 +46,12 @@ function correlation(tspan::Vector{Float64}, rho0::DenseOperator, H::Operator, J u end -function correlation(rho0::DenseOperator, H::Operator, J::Vector, - op1::Operator, op2::Operator; +function correlation(rho0::DenseOperator{B,B}, H::AbstractOperator{B,B}, J::Vector, + op1::AbstractOperator{B,B}, op2::AbstractOperator{B,B}; tol::Float64=1e-4, h0=10., - rates::Union{Vector{Float64}, Matrix{Float64}, Void}=nothing, + rates::Union{Vector{Float64}, Matrix{Float64}, Nothing}=nothing, Jdagger::Vector=dagger.(J), - kwargs...) + kwargs...) where B<:Basis op2rho0 = op2*rho0 exp1 = expect(op1, op2rho0) function fout(t, rho) @@ -94,11 +96,11 @@ automatically. * `kwargs...`: Further arguments are passed on to the ode solver. """ function spectrum(omega_samplepoints::Vector{Float64}, - H::Operator, J::Vector, op::Operator; - rho0::DenseOperator=tensor(basisstate(H.basis_l, 1), dagger(basisstate(H.basis_r, 1))), + H::AbstractOperator{B,B}, J::Vector, op::AbstractOperator{B,B}; + rho0::DenseOperator{B,B}=tensor(basisstate(H.basis_l, 1), dagger(basisstate(H.basis_r, 1))), tol::Float64=1e-4, - rho_ss::DenseOperator=steadystate.master(H, J; tol=tol, rho0=rho0)[end][end], - kwargs...) + rho_ss::DenseOperator{B,B}=steadystate.master(H, J; tol=tol, rho0=rho0)[end][end], + kwargs...) where B<:Basis domega = minimum(diff(omega_samplepoints)) dt = 2*pi/abs(omega_samplepoints[end] - omega_samplepoints[1]) T = 2*pi/domega @@ -108,11 +110,11 @@ function spectrum(omega_samplepoints::Vector{Float64}, return omega_samplepoints, S end -function spectrum(H::Operator, J::Vector, op::Operator; - rho0::DenseOperator=tensor(basisstate(H.basis_l, 1), dagger(basisstate(H.basis_r, 1))), +function spectrum(H::AbstractOperator{B,B}, J::Vector, op::AbstractOperator{B,B}; + rho0::DenseOperator{B,B}=tensor(basisstate(H.basis_l, 1), dagger(basisstate(H.basis_r, 1))), tol::Float64=1e-4, h0=10., - rho_ss::DenseOperator=steadystate.master(H, J; tol=tol)[end][end], - kwargs...) + rho_ss::DenseOperator{B,B}=steadystate.master(H, J; tol=tol)[end][end], + kwargs...) where B<:Basis tspan, exp_values = correlation(rho_ss, H, J, dagger(op), op, tol=tol, h0=h0, kwargs...) dtmin = minimum(diff(tspan)) T = tspan[end] - tspan[1] @@ -125,16 +127,16 @@ end """ - timecorrelations.correlation2spectrum(tspan, corr; normalize) + timecorrelations.correlation2spectrum(tspan, corr; normalize_spec) Calculate spectrum as Fourier transform of a correlation function with a given correlation function. # Arguments * `tspan`: List of time points corresponding to the correlation function. * `corr`: Correlation function of which the Fourier transform is to be calculated. -* `normalize`: Specify if spectrum should be normalized to its maximum. +* `normalize_spec`: Specify if spectrum should be normalized to its maximum. """ -function correlation2spectrum(tspan::Vector{Float64}, corr::Vector{T}; normalize::Bool=false) where T <: Number +function correlation2spectrum(tspan::Vector{Float64}, corr::Vector{T}; normalize_spec::Bool=false) where T <: Number n = length(tspan) if length(corr) != n ArgumentError("tspan and corr must be of same length!") @@ -152,7 +154,7 @@ function correlation2spectrum(tspan::Vector{Float64}, corr::Vector{T}; normalize omega .*= 2pi/tmax spec = 2dt.*fftshift(real(fft(corr))) - omega, normalize ? spec./maximum(spec) : spec + omega, normalize_spec ? spec./maximum(spec) : spec end diff --git a/src/timeevolution_base.jl b/src/timeevolution_base.jl index ebb1b981..365077c3 100644 --- a/src/timeevolution_base.jl +++ b/src/timeevolution_base.jl @@ -1,13 +1,14 @@ -using ..metrics +using QuantumOpticsBase +using QuantumOpticsBase: check_samebases, check_multiplicable -import OrdinaryDiffEq, DiffEqCallbacks, StochasticDiffEq +import OrdinaryDiffEq, DiffEqCallbacks -const DiffArray = Union{Vector{Complex128}, Array{Complex128, 2}} +const DiffArray = Union{Vector{ComplexF64}, Array{ComplexF64, 2}} function recast! end """ - integrate(tspan::Vector{Float64}, df::Function, x0::Vector{Complex128}, + integrate(tspan::Vector{Float64}, df::Function, x0::Vector{ComplexF64}, state::T, dstate::T, fout::Function; kwargs...) Integrate using OrdinaryDiffEq @@ -67,7 +68,7 @@ function integrate(tspan::Vector{Float64}, df::Function, x0::DiffArray, end function integrate(tspan::Vector{Float64}, df::Function, x0::DiffArray, - state::T, dstate::T, ::Void; kwargs...) where T + state::T, dstate::T, ::Nothing; kwargs...) where T function fout(t::Float64, state::T) copy(state) end @@ -82,97 +83,26 @@ end function (c::SteadyStateCondtion)(rho,t,integrator) timeevolution.recast!(rho,c.state) dt = integrator.dt - drho = metrics.tracedistance(c.rho0, c.state) + drho = tracedistance(c.rho0, c.state) c.rho0.data[:] = c.state.data drho/dt < c.tol end - +const QO_CHECKS = Ref(true) """ - integrate_stoch(tspan::Vector{Float64}, df::Function, dg::Vector{Function}, x0::Vector{Complex128}, - state::T, dstate::T, fout::Function; kwargs...) + @skiptimechecks -Integrate using StochasticDiffEq +Macro to skip checks during time-dependent problems. +Useful for `timeevolution.master_dynamic` and similar functions. """ -function integrate_stoch(tspan::Vector{Float64}, df::Function, dg::Function, x0::Vector{Complex128}, - state::T, dstate::T, fout::Function, n::Int; - save_everystep = false, callback=nothing, - alg::StochasticDiffEq.StochasticDiffEqAlgorithm=StochasticDiffEq.EM(), - noise_rate_prototype = nothing, - noise_prototype_classical = nothing, - noise=nothing, - ncb=nothing, - kwargs...) where T - - function df_(dx::Vector{Complex128}, x::Vector{Complex128}, p, t) - recast!(x, state) - recast!(dx, dstate) - df(t, state, dstate) - recast!(dstate, dx) +macro skiptimechecks(ex) + return quote + QO_CHECKS.x = false + local val = $(esc(ex)) + QO_CHECKS.x = true + val end - - function dg_(dx::Union{Vector{Complex128}, Array{Complex128, 2}}, - x::Vector{Complex128}, p, t) - recast!(x, state) - dg(dx, t, state, dstate, n) - end - - function fout_(x::Vector{Complex128}, t::Float64, integrator) - recast!(x, state) - fout(t, state) - end - - nc = isa(noise_prototype_classical, Void) ? 0 : size(noise_prototype_classical)[2] - if isa(noise, Void) && n > 0 - noise_ = StochasticDiffEq.RealWienerProcess!(0.0, randn(n + nc)) - else - noise_ = noise - end - if isa(noise_rate_prototype, Void) - if n > 1 || nc > 1 || (n > 0 && nc > 0) - noise_rate_prototype = zeros(Complex128, length(x0), n + nc) - end - end - - out_type = pure_inference(fout, Tuple{eltype(tspan),typeof(state)}) - - out = DiffEqCallbacks.SavedValues(Float64,out_type) - - scb = DiffEqCallbacks.SavingCallback(fout_,out,saveat=tspan, - save_everystep=save_everystep, - save_start = false) - - full_cb = OrdinaryDiffEq.CallbackSet(callback, ncb, scb) - - prob = StochasticDiffEq.SDEProblem{true}(df_, dg_, x0,(tspan[1],tspan[end]), - noise=noise_, - noise_rate_prototype=noise_rate_prototype) - - sol = StochasticDiffEq.solve( - prob, - alg; - reltol = 1.0e-3, - abstol = 1.0e-3, - save_everystep = false, save_start = false, - save_end = false, - callback=full_cb, kwargs...) - - out.t,out.saveval end -""" - integrate_stoch - -Define fout if it was omitted. -""" -function integrate_stoch(tspan::Vector{Float64}, df::Function, dg::Function, x0::Vector{Complex128}, - state::T, dstate::T, ::Void, n::Int; kwargs...) where T - function fout(t::Float64, state::T) - copy(state) - end - integrate_stoch(tspan, df, dg, x0, state, dstate, fout, n; kwargs...) -end - - -Base.@pure pure_inference(fout,T) = Core.Inference.return_type(fout, T) +Base.@pure pure_inference(fout,T) = Core.Compiler.return_type(fout, T) diff --git a/src/transformations.jl b/src/transformations.jl deleted file mode 100644 index 98eb1fde..00000000 --- a/src/transformations.jl +++ /dev/null @@ -1,60 +0,0 @@ -module transformations - -export transform - -import ..particle: transform - -using ..polynomials, ..particle, ..operators_dense, ..fock - - -""" - transform(b1::PositionBasis, b2::FockBasis; x0=1) - transform(b1::FockBasis, b2::PositionBasis; x0=1) - -Transformation operator between position basis and fock basis. - -The coefficients are connected via the relation -```math -ψ(x_i) = \\sum_{n=0}^N ⟨x_i|n⟩ ψ_n -``` -where ``⟨x_i|n⟩`` is the value of the n-th eigenstate of a particle in -a harmonic trap potential at position ``x``, i.e.: -```math -⟨x_i|n⟩ = π^{-\\frac{1}{4}} \\frac{e^{-\\frac{1}{2}\\left(\\frac{x}{x_0}\\right)^2}}{\\sqrt{x_0}} - \\frac{1}{\\sqrt{2^n n!}} H_n\\left(\\frac{x}{x_0}\\right) -``` -""" -function transform(b1::PositionBasis, b2::FockBasis; x0::Real=1) - T = Matrix{Complex128}(length(b1), length(b2)) - xvec = samplepoints(b1) - A = hermite.A(b2.N) - delta_x = particle.spacing(b1) - c = 1./sqrt(x0*sqrt(pi))*sqrt(delta_x) - for i in 1:length(b1) - u = xvec[i]/x0 - C = c*exp(-u^2/2) - for n=0:b2.N - T[i,n+1] = C*horner(A[n+1], u) - end - end - DenseOperator(b1, b2, T) -end - -function transform(b1::FockBasis, b2::PositionBasis; x0::Real=1) - T = Matrix{Complex128}(length(b1), length(b2)) - xvec = samplepoints(b2) - A = hermite.A(b1.N) - delta_x = particle.spacing(b2) - c = 1./sqrt(x0*sqrt(pi))*sqrt(delta_x) - for i in 1:length(b2) - u = xvec[i]/x0 - C = c*exp(-u^2/2) - for n=0:b1.N - T[n+1,i] = C*horner(A[n+1], u) - end - end - DenseOperator(b1, b2, T) -end - -end # module - diff --git a/test/runtests.jl b/test/runtests.jl index 10735496..0f68db39 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,41 +1,15 @@ names = [ - "test_sortedindices.jl", - "test_polynomials.jl", - - "test_bases.jl", - "test_states.jl", - - "test_operators.jl", - "test_operators_dense.jl", - "test_sparsematrix.jl", - "test_operators_sparse.jl", - "test_operators_lazytensor.jl", - "test_operators_lazysum.jl", - "test_operators_lazyproduct.jl", - - "test_fock.jl", - "test_spin.jl", - "test_particle.jl", - "test_manybody.jl", - "test_nlevel.jl", - "test_subspace.jl", - "test_state_definitions.jl", - "test_phasespace.jl", - "test_transformations.jl", - - "test_metrics.jl", - "test_embed.jl", "test_spectralanalysis.jl", "test_timeevolution_schroedinger.jl", "test_timeevolution_master.jl", "test_timeevolution_mcwf.jl", + "test_timeevolution_bloch_redfield.jl", "test_timeevolution_twolevel.jl", "test_timeevolution_pumpedcavity.jl", - "test_superoperators.jl", "test_steadystate.jl", "test_timecorrelations.jl", @@ -44,9 +18,7 @@ names = [ "test_stochastic_definitions.jl", "test_stochastic_schroedinger.jl", "test_stochastic_master.jl", - "test_stochastic_semiclassical.jl", - - "test_printing.jl" + "test_stochastic_semiclassical.jl" ] detected_tests = filter( diff --git a/test/test_bases.jl b/test/test_bases.jl deleted file mode 100644 index 53c68f9b..00000000 --- a/test/test_bases.jl +++ /dev/null @@ -1,52 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "basis" begin - -shape1 = [5] -shape2 = [2, 3] -shape3 = [6] - -b1 = GenericBasis(shape1) -b2 = GenericBasis(shape2) -b3 = GenericBasis(shape3) - -@test b1.shape == shape1 -@test b2.shape == shape2 -@test b1 != b2 -@test b1 != FockBasis(2) -@test b1 == b1 - -@test tensor(b1) == b1 -comp_b1 = tensor(b1, b2) -comp_uni = b1 ⊗ b2 -comp_b2 = tensor(b1, b1, b2) -@test comp_b1.shape == [prod(shape1), prod(shape2)] -@test comp_uni.shape == [prod(shape1), prod(shape2)] -@test comp_b2.shape == [prod(shape1), prod(shape1), prod(shape2)] - -@test b1^3 == CompositeBasis(b1, b1, b1) -@test (b1⊗b2)^2 == CompositeBasis(b1, b2, b1, b2) -@test_throws ArgumentError b1^(0) - -comp_b1_b2 = tensor(comp_b1, comp_b2) -@test comp_b1_b2.shape == [prod(shape1), prod(shape2), prod(shape1), prod(shape1), prod(shape2)] -@test comp_b1_b2 == CompositeBasis(b1, b2, b1, b1, b2) - -@test_throws ArgumentError tensor() -@test comp_b2.shape == tensor(b1, comp_b1).shape -@test comp_b2 == tensor(b1, comp_b1) - -@test_throws ArgumentError ptrace(comp_b1, [1, 2]) -@test ptrace(comp_b2, [1]) == ptrace(comp_b2, [2]) == comp_b1 == ptrace(comp_b2, 1) -@test ptrace(comp_b2, [1, 2]) == ptrace(comp_b1, [1]) -@test ptrace(comp_b2, [2, 3]) == ptrace(comp_b1, [2]) - -comp1 = tensor(b1, b2, b3) -comp2 = tensor(b2, b1, b3) -@test permutesystems(comp1, [2,1,3]) == comp2 - -@test !QuantumOptics.bases.equal_bases([b1, b2], [b1, b3]) -@test !QuantumOptics.bases.multiplicable(comp1, b1 ⊗ b2 ⊗ NLevelBasis(prod(b3.shape))) - -end # testset diff --git a/test/test_embed.jl b/test/test_embed.jl deleted file mode 100644 index e82c3233..00000000 --- a/test/test_embed.jl +++ /dev/null @@ -1,73 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "embed" begin - -srand(0) - -# Set up operators -spinbasis = SpinBasis(1//2) - -b1 = NLevelBasis(3) -b2 = SpinBasis(1//2) -b3 = FockBasis(2) - -I1 = full(identityoperator(b1)) -I2 = full(identityoperator(b2)) -I3 = full(identityoperator(b3)) - -b = b1 ⊗ b2 ⊗ b3 - -op1 = DenseOperator(b1, b1, rand(Complex128, length(b1), length(b1))) -op2 = DenseOperator(b2, b2, rand(Complex128, length(b2), length(b2))) -op3 = DenseOperator(b3, b3, rand(Complex128, length(b3), length(b3))) - - -# Test Vector{Int}, Vector{Operator} -x = embed(b, [1,2], [op1, op2]) -y = op1 ⊗ op2 ⊗ I3 -@test 0 ≈ abs(tracedistance_nh(x, y)) - -x = embed(b, [1,2], [sparse(op1), sparse(op2)]) -y = op1 ⊗ op2 ⊗ I3 -@test 0 ≈ abs(tracedistance_nh(full(x), y)) - -x = embed(b, 1, op1) -y = op1 ⊗ I2 ⊗ I3 -@test 0 ≈ abs(tracedistance_nh(x, y)) - -x = embed(b, 2, op2) -y = I1 ⊗ op2 ⊗ I3 -@test 0 ≈ abs(tracedistance_nh(x, y)) - -x = embed(b, 3, op3) -y = I1 ⊗ I2 ⊗ op3 -@test 0 ≈ abs(tracedistance_nh(x, y)) - - -# Test Dict(Int=>Operator) -x = embed(b, Dict(1 => sparse(op1), 2 => sparse(op2))) -y = op1 ⊗ op2 ⊗ I3 -@test 0 ≈ abs(tracedistance_nh(full(x), y)) - -x = embed(b, Dict(1 => op1, 2 => op2)) -y = op1 ⊗ op2 ⊗ I3 -@test 0 ≈ abs(tracedistance_nh(x, y)) - -x = embed(b, Dict([1,3] => sparse(op1⊗op3))) -y = op1 ⊗ I2 ⊗ op3 -@test 0 ≈ abs(tracedistance_nh(full(x), y)) - -x = embed(b, Dict([1,3] => op1⊗op3)) -y = op1 ⊗ I2 ⊗ op3 -@test 0 ≈ abs(tracedistance_nh(x, y)) - -x = embed(b, Dict([3,1] => sparse(op3⊗op1))) -y = op1 ⊗ I2 ⊗ op3 -@test 0 ≈ abs(tracedistance_nh(full(x), y)) - -x = embed(b, Dict([3,1] => op3⊗op1)) -y = op1 ⊗ I2 ⊗ op3 -@test 0 ≈ abs(tracedistance_nh(x, y)) - -end # testset diff --git a/test/test_fock.jl b/test/test_fock.jl deleted file mode 100644 index f038c5b8..00000000 --- a/test/test_fock.jl +++ /dev/null @@ -1,71 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "fock" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) -randstate(b) = normalize(Ket(b, rand(Complex128, length(b)))) -randop(bl, br) = DenseOperator(bl, br, rand(Complex128, length(bl), length(br))) -randop(b) = randop(b, b) - -basis = FockBasis(2) - -# Test creation -@test basis.N == 2 -@test basis.shape[1] == 3 -@test_throws DimensionMismatch FockBasis(-1) - -# Test equality -@test FockBasis(2) == FockBasis(2) -@test FockBasis(2) != FockBasis(3) - -# Test operators -@test number(basis) == SparseOperator(basis, spdiagm(Complex128[0, 1, 2])) -@test destroy(basis) == SparseOperator(basis, sparse(Complex128[0 1 0; 0 0 sqrt(2); 0 0 0])) -@test create(basis) == SparseOperator(basis, sparse(Complex128[0 0 0; 1 0 0; 0 sqrt(2) 0])) -@test number(basis) == dagger(number(basis)) -@test create(basis) == dagger(destroy(basis)) -@test destroy(basis) == dagger(create(basis)) -@test 1e-15 > D(create(basis)*destroy(basis), number(basis)) - -# Test application onto statevectors -@test create(basis)*fockstate(basis, 0) == fockstate(basis, 1) -@test create(basis)*fockstate(basis, 1) == sqrt(2)*fockstate(basis, 2) -@test dagger(fockstate(basis, 0))*destroy(basis) == dagger(fockstate(basis, 1)) -@test dagger(fockstate(basis, 1))*destroy(basis) == sqrt(2)*dagger(fockstate(basis, 2)) - -@test destroy(basis)*fockstate(basis, 1) == fockstate(basis, 0) -@test destroy(basis)*fockstate(basis, 2) == sqrt(2)*fockstate(basis, 1) -@test dagger(fockstate(basis, 1))*create(basis) == dagger(fockstate(basis, 0)) -@test dagger(fockstate(basis, 2))*create(basis) == sqrt(2)*dagger(fockstate(basis, 1)) - -# Test displacement operator -b = FockBasis(30) -alpha = complex(0.5, 0.3) -d = displace(b, alpha) -a = destroy(b) -@test 1e-12 > D(d*dagger(d), identityoperator(b)) -@test 1e-12 > D(dagger(d)*d, identityoperator(b)) -@test 1e-12 > D(dagger(d), displace(b, -alpha)) -@test 1e-15 > norm(coherentstate(b, alpha) - displace(b, alpha)*fockstate(b, 0)) - -# Test Fock states -b = FockBasis(5) -@test expect(number(b), fockstate(b, 3)) == complex(3.) - -# Test coherent states -b = FockBasis(100) -alpha = complex(3.) -a = destroy(b) -n = number(b) -psi = coherentstate(b, alpha) -rho = dm(psi) - -@test 1e-14 > norm(expect(a, psi) - alpha) -@test 1e-14 > norm(expect(a, rho) - alpha) -@test 1e-13 > abs(variance(n, psi) - abs(alpha)^2) -@test 1e-13 > abs(variance(n, rho) - abs(alpha)^2) - -end # testset diff --git a/test/test_manybody.jl b/test/test_manybody.jl deleted file mode 100644 index cd262092..00000000 --- a/test/test_manybody.jl +++ /dev/null @@ -1,158 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "manybody" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) -D(x1::StateVector, x2::StateVector) = norm(x2-x1) - -# Test state creation -Nmodes = 5 -b = GenericBasis(Nmodes) -@test bosonstates(b, 1) == fermionstates(b, 1) -@test bosonstates(b, 2) != fermionstates(b, 2) -@test length(bosonstates(b, 1)) == Nmodes -@test bosonstates(b, 1) ∪ bosonstates(b, 2) == bosonstates(b, [1, 2]) -@test fermionstates(b, 1) ∪ fermionstates(b, 2) == fermionstates(b, [1, 2]) - -@test ManyBodyBasis(b, bosonstates(b, 1)) == ManyBodyBasis(b, fermionstates(b, 1)) -@test ManyBodyBasis(b, bosonstates(b, 2)) != ManyBodyBasis(b, fermionstates(b, 2)) - -# Test basisstate -b_mb = ManyBodyBasis(b, bosonstates(b, 2)) -psi_mb = basisstate(b_mb, [2, 0, 0, 0, 0]) -op = dm(basisstate(b, 1)) -@test onebodyexpect(op, psi_mb) ≈ 2 -psi_mb = basisstate(b_mb, [1, 0, 1, 0, 0]) -@test onebodyexpect(op, psi_mb) ≈ 1 -@test_throws ArgumentError basisstate(b_mb, [1, 0, 0, 0, 0]) - -# Test creation operator -b_mb = ManyBodyBasis(b, bosonstates(b, [0, 1, 2])) -vac = basisstate(b_mb, [0, 0, 0, 0, 0]) -at1 = create(b_mb, 1) -at2 = create(b_mb, 2) -at3 = create(b_mb, 3) -at4 = create(b_mb, 4) -at5 = create(b_mb, 5) -@test 1e-12 > D(at1*vac, basisstate(b_mb, [1, 0, 0, 0, 0])) -@test 1e-12 > D(at2*vac, basisstate(b_mb, [0, 1, 0, 0, 0])) -@test 1e-12 > D(at3*vac, basisstate(b_mb, [0, 0, 1, 0, 0])) -@test 1e-12 > D(at4*vac, basisstate(b_mb, [0, 0, 0, 1, 0])) -@test 1e-12 > D(at5*vac, basisstate(b_mb, [0, 0, 0, 0, 1])) - -@test 1e-12 > D(at1*at1*vac, sqrt(2)*basisstate(b_mb, [2, 0, 0, 0, 0])) -@test 1e-12 > D(at1*at2*vac, basisstate(b_mb, [1, 1, 0, 0, 0])) -@test 1e-12 > norm(at1*basisstate(b_mb, [2, 0, 0, 0, 0])) - -# Test annihilation operator -b_mb = ManyBodyBasis(b, bosonstates(b, [0, 1, 2])) -vac = basisstate(b_mb, [0, 0, 0, 0, 0]) -a1 = destroy(b_mb, 1) -a2 = destroy(b_mb, 2) -a3 = destroy(b_mb, 3) -a4 = destroy(b_mb, 4) -a5 = destroy(b_mb, 5) -@test 1e-12 > D(a1*basisstate(b_mb, [1, 0, 0, 0, 0]), vac) -@test 1e-12 > D(a2*basisstate(b_mb, [0, 1, 0, 0, 0]), vac) -@test 1e-12 > D(a3*basisstate(b_mb, [0, 0, 1, 0, 0]), vac) -@test 1e-12 > D(a4*basisstate(b_mb, [0, 0, 0, 1, 0]), vac) -@test 1e-12 > D(a5*basisstate(b_mb, [0, 0, 0, 0, 1]), vac) - -@test 1e-12 > D(a1*a1*basisstate(b_mb, [2, 0, 0, 0, 0]), sqrt(2)*vac) -@test 1e-12 > D(a1*a2*basisstate(b_mb, [1, 1, 0, 0, 0]), vac) -@test 1e-12 > norm(a1*vac) - -# Test number operator -b = GenericBasis(3) -b_mb = ManyBodyBasis(b, bosonstates(b, [0, 1, 2, 3, 4])) -vac = basisstate(b_mb, [0, 0, 0]) -n = number(b_mb) -n1 = number(b_mb, 1) -n2 = number(b_mb, 2) -n3 = number(b_mb, 3) - -@test 1 ≈ expect(n2, basisstate(b_mb, [0, 1, 0])) -@test 2 ≈ expect(n3, basisstate(b_mb, [0, 0, 2])) -@test 0 ≈ expect(n1+n2+n3, vac) -@test 4 ≈ expect(n, basisstate(b_mb, [1, 3, 0])) - -psi = randstate(b_mb) -rho = randoperator(b_mb) - -n1_ = dm(basisstate(b, 1)) -n2_ = dm(basisstate(b, 2)) -n3_ = dm(basisstate(b, 3)) - -@test expect(n1, psi) ≈ onebodyexpect(n1_, psi) -@test expect(n2, psi) ≈ onebodyexpect(n2_, psi) -@test expect(n3, psi) ≈ onebodyexpect(n3_, psi) -@test expect(n1, rho) ≈ onebodyexpect(n1_, rho) -@test expect(n2, rho) ≈ onebodyexpect(n2_, rho) -@test expect(n3, rho) ≈ onebodyexpect(n3_, rho) - -# Test transition operator -b = NLevelBasis(4) -b_mb = ManyBodyBasis(b, bosonstates(b, [0, 1, 2, 3])) -t23 = transition(b_mb, 2, 3) -t32 = transition(b_mb, 3, 2) - -@test t23 == dagger(t32) -@test t32*basisstate(b_mb, [0, 1, 0, 0]) == basisstate(b_mb, [0, 0, 1, 0]) -@test 1e-12 > D(t32*basisstate(b_mb, [0, 2, 1, 0]), 2*basisstate(b_mb, [0, 1, 2, 0])) -@test 1e-12 > D(number(b_mb, 2), transition(b_mb, 2, 2)) - - -# Single particle operator in second quantization -b_single = GenericBasis(Nmodes) -b = ManyBodyBasis(b_single, bosonstates(b_single, [1, 2])) -x = randoperator(b_single) -y = randoperator(b_single) - -X = manybodyoperator(b, x) -Y = manybodyoperator(b, y) - -@test 1e-12 > D(X + Y, manybodyoperator(b, x + y)) - -x_ = sparse(x) -y_ = sparse(y) -@test 1e-12 > D(X, manybodyoperator(b, x_)) -@test 1e-12 > D(Y, manybodyoperator(b, y_)) -@test 1e-12 > D(X + Y, manybodyoperator(b, x_ + y_)) - -# Particle-particle interaction operator in second quantization -x = randoperator(b_single ⊗ b_single) -y = randoperator(b_single ⊗ b_single) - -X = manybodyoperator(b, x) -Y = manybodyoperator(b, y) - -@test 1e-12 > D(X + Y, manybodyoperator(b, x + y)) - -x_ = sparse(x) -y_ = sparse(y) -@test 1e-12 > D(X, manybodyoperator(b, x_)) -@test 1e-12 > D(Y, manybodyoperator(b, y_)) -@test 1e-12 > D(X + Y, manybodyoperator(b, x_ + y_)) - -# Test one-body expect -x = randoperator(b_single) -y = randoperator(b_single) -X = manybodyoperator(b, x) -Y = manybodyoperator(b, y) - -psi = randstate(b) - -@test onebodyexpect(x, psi) ≈ expect(X, dm(psi)) -@test onebodyexpect(x, Y) ≈ expect(X, Y) -@test onebodyexpect(sparse(x), psi) ≈ expect(X, dm(psi)) -@test onebodyexpect(sparse(x), Y) ≈ expect(X, Y) -@test onebodyexpect(x, [psi, Y]) == [onebodyexpect(x, psi), onebodyexpect(x, Y)] - -@test_throws ArgumentError manybodyoperator(b_mb, x) -@test_throws ArgumentError onebodyexpect(X, psi) -@test_throws ArgumentError onebodyexpect(X, dm(psi)) - -end # testset diff --git a/test/test_metrics.jl b/test/test_metrics.jl deleted file mode 100644 index 9d6dfe84..00000000 --- a/test/test_metrics.jl +++ /dev/null @@ -1,92 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "metrics" begin - -b1 = SpinBasis(1//2) -b2 = FockBasis(6) - -psi1 = spinup(b1) ⊗ coherentstate(b2, 0.1) -psi2 = spindown(b1) ⊗ fockstate(b2, 2) - -rho = tensor(psi1, dagger(psi1)) -sigma = tensor(psi2, dagger(psi2)) - -# tracenorm -@test tracenorm(0*rho) ≈ 0. -@test tracenorm_h(0*rho) ≈ 0. -@test tracenorm_nh(0*rho) ≈ 0. - -@test tracenorm(rho) ≈ 1. -@test tracenorm_h(rho) ≈ 1. -@test tracenorm_nh(rho) ≈ 1. - -@test_throws ArgumentError tracenorm(sparse(rho)) -@test_throws ArgumentError tracenorm_h(sparse(rho)) -@test_throws ArgumentError tracenorm_nh(sparse(rho)) - -# tracedistance -@test tracedistance(rho, sigma) ≈ 1. -@test tracedistance_h(rho, sigma) ≈ 1. -@test tracedistance_nh(rho, sigma) ≈ 1. - -@test tracedistance(rho, rho) ≈ 0. -@test tracedistance_h(rho, rho) ≈ 0. -@test tracedistance_nh(rho, rho) ≈ 0. - -@test tracedistance(sigma, sigma) ≈ 0. -@test tracedistance_h(sigma, sigma) ≈ 0. -@test tracedistance_nh(sigma, sigma) ≈ 0. - -@test_throws ArgumentError tracedistance(sparse(rho), sparse(rho)) -@test_throws ArgumentError tracedistance_h(sparse(rho), sparse(rho)) -@test_throws ArgumentError tracedistance_nh(sparse(rho), sparse(rho)) - -# tracedistance -@test tracedistance(rho, sigma) ≈ 1. -@test tracedistance(rho, rho) ≈ 0. -@test tracedistance(sigma, sigma) ≈ 0. - -rho = spinup(b1) ⊗ dagger(coherentstate(b2, 0.1)) -@test_throws bases.IncompatibleBases tracedistance(rho, rho) -@test_throws bases.IncompatibleBases tracedistance_h(rho, rho) - -@test tracedistance_nh(rho, rho) ≈ 0. - -# entropy -rho_mix = full(identityoperator(b1))/2. -@test entropy_vn(rho_mix)/log(2) ≈ 1 -psi = coherentstate(FockBasis(20), 2.0) -@test isapprox(entropy_vn(psi), 0.0, atol=1e-8) - -# fidelity -rho = tensor(psi1, dagger(psi1)) -@test fidelity(rho, rho) ≈ 1 -@test 1e-20 > abs2(fidelity(rho, sigma)) - -# ptranspose -e = spinup(b1) -g = spindown(b1) -psi3 = (e ⊗ g - g ⊗ e)/sqrt(2) - -rho = dm(psi3) -rho_pT1 = ptranspose(rho, 1) -rho_pT1_an = 0.5*(dm(e ⊗ g) + dm(g ⊗ e) - (e ⊗ e) ⊗ dagger(g ⊗ g) - (g ⊗ g) ⊗ dagger(e ⊗ e)) -rho_pT2 = ptranspose(rho, 2) -@test rho_pT1.data ≈ rho_pT1_an.data -@test rho_pT2.data ≈ rho_pT1_an.data - -@test_throws AssertionError ptranspose(e ⊗ dagger(psi1)) -@test_throws AssertionError ptranspose(dm(e)) - -rho = rho ⊗ dm(g) -@test PPT(rho, 1) == PPT(rho, 2) == false -@test PPT(rho, 3) - -@test negativity(rho, 1) == negativity(rho, 2) ≈ 0.5 -@test isapprox(negativity(rho, 3), 0.0, atol=1e-15) - -@test logarithmic_negativity(rho, 1) == logarithmic_negativity(rho, 2) ≈ 1.0 -@test isapprox(logarithmic_negativity(rho, 3), 0.0, atol=1e-15) - -end # testset diff --git a/test/test_nlevel.jl b/test/test_nlevel.jl deleted file mode 100644 index e20c3cc2..00000000 --- a/test/test_nlevel.jl +++ /dev/null @@ -1,28 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "nlevel" begin - -N = 3 -b = NLevelBasis(N) - -# Test basis equality -@test b == NLevelBasis(N) -@test b != NLevelBasis(N+1) -@test_throws DimensionMismatch NLevelBasis(0) - -# Test transition operator -@test_throws BoundsError transition(b, 0, 1) -@test_throws BoundsError transition(b, N+1, 1) -@test_throws BoundsError transition(b, 1, 0) -@test_throws BoundsError transition(b, 1, N+1) -@test full(transition(b, 2, 1)) == basisstate(b, 2) ⊗ dagger(basisstate(b, 1)) - -# Test nlevel states -@test_throws BoundsError nlevelstate(b, 0) -@test_throws BoundsError nlevelstate(b, N+1) -@test norm(nlevelstate(b, 1)) == 1. -@test norm(dagger(nlevelstate(b, 1))*nlevelstate(b, 2)) == 0. -@test norm(dagger(nlevelstate(b, 1))*transition(b, 1, 2)*nlevelstate(b, 2)) == 1. - -end # testset diff --git a/test/test_operators.jl b/test/test_operators.jl deleted file mode 100644 index 0c98438a..00000000 --- a/test/test_operators.jl +++ /dev/null @@ -1,73 +0,0 @@ -using Base.Test -using QuantumOptics - - -mutable struct test_operators <: Operator - basis_l::Basis - basis_r::Basis - data::Matrix{Complex128} - test_operators(b1::Basis, b2::Basis, data) = length(b1) == size(data, 1) && length(b2) == size(data, 2) ? new(b1, b2, data) : throw(DimensionMismatch()) -end - -@testset "operators" begin - -srand(0) - -b1 = GenericBasis(5) -b2 = GenericBasis(3) -b = b1 ⊗ b2 -op1 = randoperator(b1) -op = randoperator(b, b) -op_test = test_operators(b, b, op.data) -op_test2 = test_operators(b1, b, randoperator(b1, b).data) -op_test3 = test_operators(b1 ⊗ b2, b2 ⊗ b1, randoperator(b, b).data) -ψ = randstate(b) -ρ = randoperator(b) - -@test basis(op1) == b1 -@test length(op1) == length(op1.data) == 25 - -@test_throws ArgumentError op_test*op_test -@test_throws ArgumentError -op_test - -@test_throws ArgumentError 1 + op_test -@test_throws ArgumentError op_test + 1 -@test_throws ArgumentError 1 - op_test -@test_throws ArgumentError op_test - 1 - -@test_throws ArgumentError dagger(op_test) -@test_throws ArgumentError identityoperator(test_operators, b, b) -@test_throws ArgumentError trace(op_test) -@test_throws ArgumentError ptrace(op_test, [1]) -@test_throws ArgumentError ishermitian(op_test) -@test_throws ArgumentError full(op_test) -@test_throws ArgumentError sparse(op_test) - -@test expect(1, op1, ρ) ≈ expect(embed(b, 1, op1), ρ) -@test expect(1, op1, ψ) ≈ expect(embed(b, 1, op1), ψ) -@test expect(op, [ρ, ρ]) == [expect(op, ρ) for i=1:2] -@test expect(1, op1, [ρ, ψ]) == [expect(1, op1, ρ), expect(1, op1, ψ)] - -@test variance(1, op1, ρ) ≈ variance(embed(b, 1, op1), ρ) -@test variance(1, op1, ψ) ≈ variance(embed(b, 1, op1), ψ) -@test variance(op, [ρ, ρ]) == [variance(op, ρ) for i=1:2] -@test variance(1, op1, [ρ, ψ]) == [variance(1, op1, ρ), variance(1, op1, ψ)] - -@test tensor(op_test) === op_test -@test_throws ArgumentError tensor(op_test, op_test) -@test_throws ArgumentError permutesystems(op_test, [1, 2]) - -@test embed(b, b, 1, op) == embed(b, 1, op) -@test embed(b, Dict{Vector{Int}, SparseOperator}()) == identityoperator(b) - -@test_throws ErrorException QuantumOptics.operators.gemm!() -@test_throws ErrorException QuantumOptics.operators.gemv!() - -@test_throws ArgumentError expm(sparse(op1)) - -@test one(b1).data == diagm(ones(b1.shape[1])) -@test one(op1).data == diagm(ones(b1.shape[1])) - -@test_throws ArgumentError conj!(op_test) - -end # testset diff --git a/test/test_operators_dense.jl b/test/test_operators_dense.jl deleted file mode 100644 index 73c4448e..00000000 --- a/test/test_operators_dense.jl +++ /dev/null @@ -1,336 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "operators-dense" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) -D(x1::StateVector, x2::StateVector) = norm(x2-x1) - -b1a = GenericBasis(2) -b1b = GenericBasis(3) -b2a = GenericBasis(1) -b2b = GenericBasis(4) -b3a = GenericBasis(1) -b3b = GenericBasis(5) - -b_l = b1a⊗b2a⊗b3a -b_r = b1b⊗b2b⊗b3b - -# Test creation -@test_throws DimensionMismatch DenseOperator(b1a, [1 1 1; 1 1 1]) -@test_throws DimensionMismatch DenseOperator(b1a, b1b, [1 1; 1 1; 1 1]) -op1 = DenseOperator(b1a, b1b, [1 1 1; 1 1 1]) -op2 = DenseOperator(b1b, b1a, [1 1; 1 1; 1 1]) -@test op1 == dagger(op2) - -# Test copy -op1 = randoperator(b1a) -op2 = copy(op1) -@test op1.data == op2.data -@test !(op1.data === op2.data) -op2.data[1,1] = complex(10.) -@test op1.data[1,1] != op2.data[1,1] - - -# Arithmetic operations -# ===================== -op_zero = DenseOperator(b_l, b_r) -op1 = randoperator(b_l, b_r) -op2 = randoperator(b_l, b_r) -op3 = randoperator(b_l, b_r) - -x1 = Ket(b_r, rand(Complex128, length(b_r))) -x2 = Ket(b_r, rand(Complex128, length(b_r))) - -xbra1 = Bra(b_l, rand(Complex128, length(b_l))) -xbra2 = Bra(b_l, rand(Complex128, length(b_l))) - -# Addition -@test_throws bases.IncompatibleBases op1 + dagger(op2) -@test 1e-14 > D(op1 + op_zero, op1) -@test 1e-14 > D(op1 + op2, op2 + op1) -@test 1e-14 > D(op1 + (op2 + op3), (op1 + op2) + op3) - -# Subtraction -@test_throws bases.IncompatibleBases op1 - dagger(op2) -@test 1e-14 > D(op1-op_zero, op1) -@test 1e-14 > D(op1-op2, op1 + (-op2)) -@test 1e-14 > D(op1-op2, op1 + (-1*op2)) -@test 1e-14 > D(op1-op2-op3, op1-(op2+op3)) - -# Test multiplication -@test_throws bases.IncompatibleBases op1*op2 -@test 1e-11 > D(op1*(x1 + 0.3*x2), op1*x1 + 0.3*op1*x2) -@test 1e-11 > D((op1+op2)*(x1+0.3*x2), op1*x1 + 0.3*op1*x2 + op2*x1 + 0.3*op2*x2) - -@test 1e-11 > D((xbra1+0.3*xbra2)*op1, xbra1*op1 + 0.3*xbra2*op1) -@test 1e-11 > D((xbra1+0.3*xbra2)*(op1+op2), xbra1*op1 + 0.3*xbra2*op1 + xbra1*op2 + 0.3*xbra2*op2) - -@test 1e-12 > D(op1*dagger(0.3*op2), 0.3*dagger(op2*dagger(op1))) -@test 1e-12 > D((op1 + op2)*dagger(0.3*op3), 0.3*op1*dagger(op3) + 0.3*op2*dagger(op3)) -@test 1e-12 > D(0.3*(op1*dagger(op2)), op1*(0.3*dagger(op2))) - -tmp = copy(op1) -conj!(tmp) -@test tmp == conj(op1) && conj(tmp.data) == op1.data - -# Internal layout -b1 = GenericBasis(2) -b2 = GenericBasis(3) -b3 = GenericBasis(4) -op1 = randoperator(b1, b2) -op2 = randoperator(b2, b3) -x1 = randstate(b2) -d1 = op1.data -d2 = op2.data -v = x1.data -@test (op1*x1).data ≈ [d1[1,1]*v[1] + d1[1,2]*v[2] + d1[1,3]*v[3], d1[2,1]*v[1] + d1[2,2]*v[2] + d1[2,3]*v[3]] -@test (op1*op2).data[2,3] ≈ d1[2,1]*d2[1,3] + d1[2,2]*d2[2,3] + d1[2,3]*d2[3,3] - -# Test division -@test 1e-14 > D(op1/7, (1/7)*op1) - -# Tensor product -# ============== -op1a = randoperator(b1a, b1b) -op1b = randoperator(b1a, b1b) -op2a = randoperator(b2a, b2b) -op2b = randoperator(b2a, b2b) -op3a = randoperator(b3a, b3b) -op123 = op1a ⊗ op2a ⊗ op3a -@test op123.basis_l == b_l -@test op123.basis_r == b_r - -# Associativity -@test 1e-13 > D((op1a ⊗ op2a) ⊗ op3a, op1a ⊗ (op2a ⊗ op3a)) - -# Linearity -@test 1e-13 > D(op1a ⊗ (0.3*op2a), 0.3*(op1a ⊗ op2a)) -@test 1e-13 > D((0.3*op1a) ⊗ op2a, 0.3*(op1a ⊗ op2a)) - -# Distributivity -@test 1e-13 > D(op1a ⊗ (op2a + op2b), op1a ⊗ op2a + op1a ⊗ op2b) -@test 1e-13 > D((op2a + op2b) ⊗ op3a, op2a ⊗ op3a + op2b ⊗ op3a) - -# Mixed-product property -@test 1e-13 > D((op1a ⊗ op2a) * dagger(op1b ⊗ op2b), (op1a*dagger(op1b)) ⊗ (op2a*dagger(op2b))) - -# Transpose -@test 1e-13 > D(dagger(op1a ⊗ op2a), dagger(op1a) ⊗ dagger(op2a)) - -# Internal layout -a = Ket(b1a, rand(Complex128, length(b1a))) -b = Ket(b2b, rand(Complex128, length(b2b))) -ab = a ⊗ dagger(b) -@test ab.data[2,3] == a.data[2]*conj(b.data[3]) -@test ab.data[2,1] == a.data[2]*conj(b.data[1]) - -shape = tuple(op123.basis_l.shape..., op123.basis_r.shape...) -idx = sub2ind(shape, 2, 1, 1, 3, 4, 5) -@test op123.data[idx] == op1a.data[2,3]*op2a.data[1,4]*op3a.data[1,5] -@test reshape(op123.data, shape...)[2, 1, 1, 3, 4, 5] == op1a.data[2,3]*op2a.data[1,4]*op3a.data[1,5] - -idx = sub2ind(shape, 2, 1, 1, 1, 3, 4) -@test op123.data[idx] == op1a.data[2,1]*op2a.data[1,3]*op3a.data[1,4] -@test reshape(op123.data, shape...)[2, 1, 1, 1, 3, 4] == op1a.data[2,1]*op2a.data[1,3]*op3a.data[1,4] - - -# Test identityoperator -x1 = Ket(b_r, rand(Complex128, length(b_r))) -x2 = Ket(b_r, rand(Complex128, length(b_r))) -xbra1 = Bra(b_l, rand(Complex128, length(b_l))) -xbra2 = Bra(b_l, rand(Complex128, length(b_l))) - -I = identityoperator(DenseOperator, b_r) -@test isa(I, DenseOperator) -@test identityoperator(SparseOperator, b_r) == sparse(I) -@test 1e-11 > D(I*x1, x1) -@test I == identityoperator(DenseOperator, b1b) ⊗ identityoperator(DenseOperator, b2b) ⊗ identityoperator(DenseOperator, b3b) - -I = identityoperator(DenseOperator, b_l) -@test isa(I, DenseOperator) -@test identityoperator(SparseOperator, b_l) == sparse(I) -@test 1e-11 > D(xbra1*I, xbra1) -@test I == identityoperator(DenseOperator, b1a) ⊗ identityoperator(DenseOperator, b2a) ⊗ identityoperator(DenseOperator, b3a) - -# Test trace and normalize -op = DenseOperator(GenericBasis(3), [1 3 2;5 2 2;-1 2 5]) -@test 8 == trace(op) -op_normalized = normalize(op) -@test 8 == trace(op) -@test 1 == trace(op_normalized) -op_copy = deepcopy(op) -normalize!(op_copy) -@test trace(op) != trace(op_copy) -@test 1 ≈ trace(op_copy) - -# Test partial trace of state vectors -psi1 = 0.1*randstate(b1a) -psi2 = 0.3*randstate(b2a) -psi3 = 0.7*randstate(b3a) -psi12 = psi1 ⊗ psi2 -psi13 = psi1 ⊗ psi3 -psi23 = psi2 ⊗ psi3 -psi123 = psi1 ⊗ psi2 ⊗ psi3 - -@test 1e-13 > D(0.1^2*0.3^2*psi3 ⊗ dagger(psi3), ptrace(psi123, [1, 2])) -@test 1e-13 > D(0.1^2*0.7^2*psi2 ⊗ dagger(psi2), ptrace(psi123, [1, 3])) -@test 1e-13 > D(0.3^2*0.7^2*psi1 ⊗ dagger(psi1), ptrace(psi123, [2, 3])) - -@test 1e-13 > D(0.1^2*psi23 ⊗ dagger(psi23), ptrace(psi123, 1)) -@test 1e-13 > D(0.3^2*psi13 ⊗ dagger(psi13), ptrace(psi123, 2)) -@test 1e-13 > D(0.7^2*psi12 ⊗ dagger(psi12), ptrace(psi123, 3)) - -@test 1e-13 > D(ptrace(psi123, [1, 2]), dagger(ptrace(dagger(psi123), [1, 2]))) -@test 1e-13 > D(ptrace(psi123, 3), dagger(ptrace(dagger(psi123), 3))) - -@test_throws ArgumentError ptrace(psi123, [1, 2, 3]) - -# Test partial trace of operators -b1 = GenericBasis(3) -b2 = GenericBasis(5) -b3 = GenericBasis(7) -op1 = randoperator(b1) -op2 = randoperator(b2) -op3 = randoperator(b3) -op123 = op1 ⊗ op2 ⊗ op3 - -@test 1e-13 > D(op1⊗op2*trace(op3), ptrace(op123, 3)) -@test 1e-13 > D(op1⊗op3*trace(op2), ptrace(op123, 2)) -@test 1e-13 > D(op2⊗op3*trace(op1), ptrace(op123, 1)) - -@test 1e-13 > D(op1*trace(op2)*trace(op3), ptrace(op123, [2,3])) -@test 1e-13 > D(op2*trace(op1)*trace(op3), ptrace(op123, [1,3])) -@test 1e-13 > D(op3*trace(op1)*trace(op2), ptrace(op123, [1,2])) - -@test_throws ArgumentError ptrace(op123, [1,2,3]) -x = randoperator(b1, b1⊗b2) -@test_throws ArgumentError ptrace(x, [1]) -x = randoperator(b1⊗b1⊗b2, b1⊗b2) -@test_throws ArgumentError ptrace(x, [1, 2]) -x = randoperator(b1⊗b2) -@test_throws ArgumentError ptrace(x, [1, 2]) -x = randoperator(b1⊗b2, b2⊗b1) -@test_throws ArgumentError ptrace(x, [1]) - -op1 = randoperator(b1, b2) -op2 = randoperator(b3) - -@test 1e-13 > D(op1*trace(op2), ptrace(op1⊗op2, 2)) - -# Test expect -b1 = GenericBasis(3) -b2 = GenericBasis(5) -b3 = GenericBasis(7) -op1 = randoperator(b1) -op2 = randoperator(b2) -op3 = randoperator(b3) -op123 = op1 ⊗ op2 ⊗ op3 -b_l = b1 ⊗ b2 ⊗ b3 - -state = randstate(b_l) -@test expect(op123, state) ≈ dagger(state)*op123*state -@test expect(1, op1, state) ≈ expect(op1, ptrace(state, [2, 3])) -@test expect(2, op2, state) ≈ expect(op2, ptrace(state, [1, 3])) -@test expect(3, op3, state) ≈ expect(op3, ptrace(state, [1, 2])) - -state = randoperator(b_l) -@test expect(op123, state) ≈ trace(op123*state) -@test expect(1, op1, state) ≈ expect(op1, ptrace(state, [2, 3])) -@test expect(2, op2, state) ≈ expect(op2, ptrace(state, [1, 3])) -@test expect(3, op3, state) ≈ expect(op3, ptrace(state, [1, 2])) - -# Permute systems -op1 = randoperator(b1a, b1b) -op2 = randoperator(b2a, b2b) -op3 = randoperator(b3a, b3b) -op123 = op1⊗op2⊗op3 - -op132 = op1⊗op3⊗op2 -@test 1e-14 > D(permutesystems(op123, [1, 3, 2]), op132) - -op213 = op2⊗op1⊗op3 -@test 1e-14 > D(permutesystems(op123, [2, 1, 3]), op213) - -op231 = op2⊗op3⊗op1 -@test 1e-14 > D(permutesystems(op123, [2, 3, 1]), op231) - -op312 = op3⊗op1⊗op2 -@test 1e-14 > D(permutesystems(op123, [3, 1, 2]), op312) - -op321 = op3⊗op2⊗op1 -@test 1e-14 > D(permutesystems(op123, [3, 2, 1]), op321) - - -# Test projector -xket = normalize(Ket(b_l, rand(Complex128, length(b_l)))) -yket = normalize(Ket(b_l, rand(Complex128, length(b_l)))) -xbra = dagger(xket) -ybra = dagger(yket) - -@test 1e-13 > D(projector(xket)*xket, xket) -@test 1e-13 > D(xbra*projector(xket), xbra) -@test 1e-13 > D(projector(xbra)*xket, xket) -@test 1e-13 > D(xbra*projector(xbra), xbra) -@test 1e-13 > D(ybra*projector(yket, xbra), xbra) -@test 1e-13 > D(projector(yket, xbra)*xket, yket) - -# Test operator exponential -op = randoperator(b1a) -@test 1e-13 > D(op^2, op*op) -@test 1e-13 > D(op^3, op*op*op) -@test 1e-13 > D(op^4, op*op*op*op) - -# Test gemv -b1 = GenericBasis(3) -b2 = GenericBasis(5) -op = randoperator(b1, b2) -xket = randstate(b2) -xbra = dagger(randstate(b1)) -rket = randstate(b1) -rbra = dagger(randstate(b2)) -alpha = complex(0.7, 1.5) -beta = complex(0.3, 2.1) - -rket_ = deepcopy(rket) -operators.gemv!(complex(1.0), op, xket, complex(0.), rket_) -@test 0 ≈ D(rket_, op*xket) - -rket_ = deepcopy(rket) -operators.gemv!(alpha, op, xket, beta, rket_) -@test 1e-13 > D(rket_, alpha*op*xket + beta*rket) - -rbra_ = deepcopy(rbra) -operators.gemv!(complex(1.0), xbra, op, complex(0.), rbra_) -@test 0 ≈ D(rbra_, xbra*op) - -rbra_ = deepcopy(rbra) -operators.gemv!(alpha, xbra, op, beta, rbra_) -@test 1e-13 > D(rbra_, alpha*xbra*op + beta*rbra) - -# # Test gemm -b1 = GenericBasis(37) -b2 = GenericBasis(53) -b3 = GenericBasis(41) -op1 = randoperator(b1, b2) -op2 = randoperator(b2, b3) -r = randoperator(b1, b3) -alpha = complex(0.7, 1.5) -beta = complex(0.3, 2.1) - -r_ = deepcopy(r) -operators.gemm!(complex(1.0), op1, op2, complex(0.), r_) -@test 1e-13 > D(r_, op1*op2) - -r_ = deepcopy(r) -operators.gemm!(alpha, op1, op2, beta, r_) -@test 1e-10 > D(r_, alpha*op1*op2 + beta*r) - -dat = rand(prod(b_r.shape)) -x = Ket(b_r, dat) -y = Bra(b_r, dat) -@test dm(x) == dm(y) - -end # testset diff --git a/test/test_operators_lazyproduct.jl b/test/test_operators_lazyproduct.jl deleted file mode 100644 index 95a15051..00000000 --- a/test/test_operators_lazyproduct.jl +++ /dev/null @@ -1,184 +0,0 @@ -using Base.Test -using QuantumOptics - - -@testset "operators-lazyproduct" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) -D(x1::StateVector, x2::StateVector) = norm(x2-x1) - -b1a = GenericBasis(2) -b1b = GenericBasis(3) -b2a = GenericBasis(1) -b2b = GenericBasis(4) -b3a = GenericBasis(1) -b3b = GenericBasis(5) - -b_l = b1a⊗b2a⊗b3a -b_r = b1b⊗b2b⊗b3b - -# Test creation -@test_throws ArgumentError LazyProduct() -@test_throws bases.IncompatibleBases LazyProduct(randoperator(b_l, b_r), randoperator(b_l, b_r)) -@test_throws bases.IncompatibleBases LazyProduct(randoperator(b_l, b_r), sparse(randoperator(b_l, b_r))) - -# Test copy -op1 = 2*LazyProduct(randoperator(b_l, b_r), sparse(randoperator(b_r, b_l))) -op2 = copy(op1) -@test op1 == op2 -@test !(op1 === op2) -op2.operators[1].data[1,1] = complex(10.) -@test op1.operators[1].data[1,1] != op2.operators[1].data[1,1] -op2.factor = 3. -@test op2.factor != op1.factor - -# Test full & sparse -op1 = randoperator(b_l, b_r) -op2 = randoperator(b_r, b_l) -@test 0.1*(op1*op2) == full(LazyProduct([sparse(op1), sparse(op2)], 0.1)) -@test 0.1*(sparse(op1)*sparse(op2)) == sparse(LazyProduct([op1, op2], 0.1)) - - -# Arithmetic operations -# ===================== -op1a = randoperator(b_l, b_r) -op1b = randoperator(b_r, b_l) -op2a = randoperator(b_l, b_r) -op2b = randoperator(b_r, b_l) -op3a = randoperator(b_l, b_l) -op1 = LazyProduct([op1a, sparse(op1b)])*0.1 -op1_ = 0.1*(op1a*op1b) -op2 = LazyProduct([sparse(op2a), op2b], 0.3) -op2_ = 0.3*(op2a*op2b) -op3 = LazyProduct(op3a) -op3_ = op3a - -x1 = Ket(b_l, rand(Complex128, length(b_l))) -x2 = Ket(b_l, rand(Complex128, length(b_l))) -xbra1 = Bra(b_l, rand(Complex128, length(b_l))) -xbra2 = Bra(b_l, rand(Complex128, length(b_l))) - -# Addition -@test_throws ArgumentError op1 + op2 -@test 1e-14 > D(-op1_, -op1) - -# Test multiplication -@test_throws bases.IncompatibleBases op1a*op1a -@test 1e-11 > D(op1*(x1 + 0.3*x2), op1_*(x1 + 0.3*x2)) -@test 1e-11 > D((xbra1 + 0.3*xbra2)*op1, (xbra1 + 0.3*xbra2)*op1_) -@test 1e-11 > D(op1*x1 + 0.3*op1*x2, op1_*x1 + 0.3*op1_*x2) -@test 1e-12 > D(dagger(x1)*dagger(0.3*op2), dagger(x1)*dagger(0.3*op2_)) -@test 0.3*LazyProduct(op1, sparse(op2)) == LazyProduct([op1, sparse(op2)], 0.3) -@test 0.3*LazyProduct(op1)*LazyProduct(sparse(op2)) == LazyProduct([op1, sparse(op2)], 0.3) - -# Test division -@test 1e-14 > D(op1/7, op1_/7) - -# Test identityoperator -Idense = identityoperator(DenseOperator, b_l) -I = identityoperator(LazyProduct, b_l) -@test isa(I, LazyProduct) -@test full(I) == Idense -@test 1e-11 > D(I*x1, x1) -@test 1e-11 > D(xbra1*I, xbra1) - -# Test trace and normalize -op1 = randoperator(b_l) -op2 = randoperator(b_l) -op = LazyProduct(op1, op2) -@test_throws ArgumentError trace(op) -@test_throws ArgumentError ptrace(op, [1, 2]) -@test_throws ArgumentError normalize(op) -@test_throws ArgumentError normalize!(op) - -# Test expect -op1 = randoperator(b_l) -op2 = randoperator(b_l) -op = 0.3*LazyProduct(op1, sparse(op2)) -op_ = 0.3*op1*op2 - -state = Ket(b_l, rand(Complex128, length(b_l))) -@test expect(op, state) ≈ expect(op_, state) - -state = DenseOperator(b_l, b_l, rand(Complex128, length(b_l), length(b_l))) -@test expect(op, state) ≈ expect(op_, state) - -# Permute systems -op1 = randoperator(b_l) -op2 = randoperator(b_l) -op3 = randoperator(b_l) -op = 0.3*LazyProduct(op1, op2, sparse(op3)) -op_ = 0.3*op1*op2*op3 - -@test 1e-14 > D(permutesystems(op, [1, 3, 2]), permutesystems(op_, [1, 3, 2])) -@test 1e-14 > D(permutesystems(op, [2, 1, 3]), permutesystems(op_, [2, 1, 3])) -@test 1e-14 > D(permutesystems(op, [2, 3, 1]), permutesystems(op_, [2, 3, 1])) -@test 1e-14 > D(permutesystems(op, [3, 1, 2]), permutesystems(op_, [3, 1, 2])) -@test 1e-14 > D(permutesystems(op, [3, 2, 1]), permutesystems(op_, [3, 2, 1])) - - -# Test gemv -op1 = randoperator(b_l, b_r) -op2 = randoperator(b_r, b_l) -op3 = randoperator(b_l, b_r) -op = LazyProduct([op1, sparse(op2), op3], 0.2) -op_ = 0.2*op1*op2*op3 - -state = Ket(b_r, rand(Complex128, length(b_r))) -result_ = Ket(b_l, rand(Complex128, length(b_l))) -result = copy(result_) -operators.gemv!(complex(1.), op, state, complex(0.), result) -@test 1e-11 > D(result, op_*state) - -result = copy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemv!(alpha, op, state, beta, result) -@test 1e-11 > D(result, alpha*op_*state + beta*result_) - -state = Bra(b_l, rand(Complex128, length(b_l))) -result_ = Bra(b_r, rand(Complex128, length(b_r))) -result = copy(result_) -operators.gemv!(complex(1.), state, op, complex(0.), result) -@test 1e-11 > D(result, state*op_) - -result = copy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemv!(alpha, state, op, beta, result) -@test 1e-11 > D(result, alpha*state*op_ + beta*result_) - -# Test gemm -op1 = randoperator(b_l, b_r) -op2 = randoperator(b_r, b_l) -op3 = randoperator(b_l, b_r) -op = LazyProduct([op1, sparse(op2), op3], 0.2) -op_ = 0.2*op1*op2*op3 - -state = randoperator(b_r, b_r) -result_ = randoperator(b_l, b_r) -result = copy(result_) -operators.gemm!(complex(1.), op, state, complex(0.), result) -@test 1e-11 > D(result, op_*state) - -result = copy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, op, state, beta, result) -@test 1e-11 > D(result, alpha*op_*state + beta*result_) - -state = randoperator(b_l, b_l) -result_ = randoperator(b_l, b_r) -result = copy(result_) -operators.gemm!(complex(1.), state, op, complex(0.), result) -@test 1e-11 > D(result, state*op_) - -result = copy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, state, op, beta, result) -@test 1e-11 > D(result, alpha*state*op_ + beta*result_) - -end # testset diff --git a/test/test_operators_lazysum.jl b/test/test_operators_lazysum.jl deleted file mode 100644 index f648f0fb..00000000 --- a/test/test_operators_lazysum.jl +++ /dev/null @@ -1,223 +0,0 @@ -using Base.Test -using QuantumOptics - - -@testset "operators-lazysum" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) -D(x1::StateVector, x2::StateVector) = norm(x2-x1) - -b1a = GenericBasis(2) -b1b = GenericBasis(3) -b2a = GenericBasis(1) -b2b = GenericBasis(4) -b3a = GenericBasis(1) -b3b = GenericBasis(5) - -b_l = b1a⊗b2a⊗b3a -b_r = b1b⊗b2b⊗b3b - -# Test creation -@test_throws AssertionError LazySum() -@test_throws AssertionError LazySum([1., 2.], [randoperator(b_l)]) -@test_throws AssertionError LazySum(randoperator(b_l, b_r), sparse(randoperator(b_l, b_l))) -@test_throws AssertionError LazySum(randoperator(b_l, b_r), sparse(randoperator(b_r, b_r))) - -# Test copy -op1 = 2*LazySum(randoperator(b_l, b_r), sparse(randoperator(b_l, b_r))) -op2 = copy(op1) -@test op1 == op2 -@test !(op1 === op2) -op2.operators[1].data[1,1] = complex(10.) -@test op1.operators[1].data[1,1] != op2.operators[1].data[1,1] -op2.factors[1] = 3. -@test op2.factors[1] != op1.factors[1] - -# Test full & sparse -op1 = randoperator(b_l, b_r) -op2 = sparse(randoperator(b_l, b_r)) -@test 0.1*op1 + 0.3*full(op2) == full(LazySum([0.1, 0.3], [op1, op2])) -@test 0.1*sparse(op1) + 0.3*op2 == sparse(LazySum([0.1, 0.3], [op1, op2])) - - -# Arithmetic operations -# ===================== -op1a = randoperator(b_l, b_r) -op1b = randoperator(b_l, b_r) -op2a = randoperator(b_l, b_r) -op2b = randoperator(b_l, b_r) -op3a = randoperator(b_l, b_r) -op1 = LazySum([0.1, 0.3], [op1a, sparse(op1b)]) -op1_ = 0.1*op1a + 0.3*op1b -op2 = LazySum([0.7, 0.9], [sparse(op2a), op2b]) -op2_ = 0.7*op2a + 0.9*op2b -op3 = LazySum(op3a) -op3_ = op3a - -x1 = Ket(b_r, rand(Complex128, length(b_r))) -x2 = Ket(b_r, rand(Complex128, length(b_r))) -xbra1 = Bra(b_l, rand(Complex128, length(b_l))) - -# Addition -@test_throws bases.IncompatibleBases op1 + dagger(op2) -@test 1e-14 > D(op1+op2, op1_+op2_) - -# Subtraction -@test_throws bases.IncompatibleBases op1 - dagger(op2) -@test 1e-14 > D(op1 - op2, op1_ - op2_) -@test 1e-14 > D(op1 + (-op2), op1_ - op2_) -@test 1e-14 > D(op1 + (-1*op2), op1_ - op2_) - -# Test multiplication -@test_throws ArgumentError op1*op2 -@test LazySum([0.1, 0.1], [op1a, op2a]) == LazySum(op1a, op2a)*0.1 -@test LazySum([0.1, 0.1], [op1a, op2a]) == 0.1*LazySum(op1a, op2a) -@test 1e-11 > D(op1*(x1 + 0.3*x2), op1_*(x1 + 0.3*x2)) -@test 1e-11 > D(op1*x1 + 0.3*op1*x2, op1_*x1 + 0.3*op1_*x2) -@test 1e-11 > D((op1+op2)*(x1+0.3*x2), (op1_+op2_)*(x1+0.3*x2)) -@test 1e-12 > D(dagger(x1)*dagger(0.3*op2), dagger(x1)*dagger(0.3*op2_)) - -# Test division -@test 1e-14 > D(op1/7, op1_/7) - -# Test identityoperator -Idense = identityoperator(DenseOperator, b_r) -I = identityoperator(LazySum, b_r) -@test isa(I, LazySum) -@test full(I) == Idense -@test 1e-11 > D(I*x1, x1) - -Idense = identityoperator(DenseOperator, b_l) -I = identityoperator(LazySum, b_l) -@test isa(I, LazySum) -@test full(I) == Idense -@test 1e-11 > D(xbra1*I, xbra1) - -# Test trace and normalize -op1 = randoperator(b_l) -op2 = randoperator(b_l) -op3 = randoperator(b_l) -op = LazySum([0.1, 0.3, 1.2], [op1, op2, op3]) -op_ = 0.1*op1 + 0.3*op2 + 1.2*op3 - -@test trace(op_) ≈ trace(op) -op_normalized = normalize(op) -@test trace(op_) ≈ trace(op) -@test 1 ≈ trace(op_normalized) -op_copy = deepcopy(op) -normalize!(op_copy) -@test trace(op) != trace(op_copy) -@test 1 ≈ trace(op_copy) - -# Test partial trace -op1 = randoperator(b_l) -op2 = randoperator(b_l) -op3 = randoperator(b_l) -op123 = LazySum([0.1, 0.3, 1.2], [op1, op2, op3]) -op123_ = 0.1*op1 + 0.3*op2 + 1.2*op3 - -@test 1e-14 > D(ptrace(op123_, 3), ptrace(op123, 3)) -@test 1e-14 > D(ptrace(op123_, 2), ptrace(op123, 2)) -@test 1e-14 > D(ptrace(op123_, 1), ptrace(op123, 1)) - -@test 1e-14 > D(ptrace(op123_, [2,3]), ptrace(op123, [2,3])) -@test 1e-14 > D(ptrace(op123_, [1,3]), ptrace(op123, [1,3])) -@test 1e-14 > D(ptrace(op123_, [1,2]), ptrace(op123, [1,2])) - -@test_throws ArgumentError ptrace(op123, [1,2,3]) - -# Test expect -state = Ket(b_l, rand(Complex128, length(b_l))) -@test expect(op123, state) ≈ expect(op123_, state) - -state = DenseOperator(b_l, b_l, rand(Complex128, length(b_l), length(b_l))) -@test expect(op123, state) ≈ expect(op123_, state) - -# Permute systems -op1a = randoperator(b1a) -op2a = randoperator(b2a) -op3a = randoperator(b3a) -op1b = randoperator(b1a) -op2b = randoperator(b2a) -op3b = randoperator(b3a) -op1c = randoperator(b1a) -op2c = randoperator(b2a) -op3c = randoperator(b3a) -op123a = op1a⊗op2a⊗op3a -op123b = op1b⊗op2b⊗op3b -op123c = op1c⊗op2c⊗op3c -op = LazySum([0.3, 0.7, 1.2], [op123a, sparse(op123b), op123c]) -op_ = 0.3*op123a + 0.7*op123b + 1.2*op123c - -@test 1e-14 > D(permutesystems(op, [1, 3, 2]), permutesystems(op_, [1, 3, 2])) -@test 1e-14 > D(permutesystems(op, [2, 1, 3]), permutesystems(op_, [2, 1, 3])) -@test 1e-14 > D(permutesystems(op, [2, 3, 1]), permutesystems(op_, [2, 3, 1])) -@test 1e-14 > D(permutesystems(op, [3, 1, 2]), permutesystems(op_, [3, 1, 2])) -@test 1e-14 > D(permutesystems(op, [3, 2, 1]), permutesystems(op_, [3, 2, 1])) - - -# Test gemv -op1 = randoperator(b_l, b_r) -op2 = randoperator(b_l, b_r) -op3 = randoperator(b_l, b_r) -op = LazySum([0.1, 0.3, 1.2], [op1, op2, op3]) -op_ = 0.1*op1 + 0.3*op2 + 1.2*op3 - -state = Ket(b_r, rand(Complex128, length(b_r))) -result_ = Ket(b_l, rand(Complex128, length(b_l))) -result = deepcopy(result_) -operators.gemv!(complex(1.), op, state, complex(0.), result) -@test 1e-13 > D(result, op_*state) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemv!(alpha, op, state, beta, result) -@test 1e-13 > D(result, alpha*op_*state + beta*result_) - -state = Bra(b_l, rand(Complex128, length(b_l))) -result_ = Bra(b_r, rand(Complex128, length(b_r))) -result = deepcopy(result_) -operators.gemv!(complex(1.), state, op, complex(0.), result) -@test 1e-13 > D(result, state*op_) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemv!(alpha, state, op, beta, result) -@test 1e-13 > D(result, alpha*state*op_ + beta*result_) - -# Test gemm -op1 = randoperator(b_l, b_r) -op2 = randoperator(b_l, b_r) -op3 = randoperator(b_l, b_r) -op = LazySum([0.1, 0.3, 1.2], [op1, op2, op3]) -op_ = 0.1*op1 + 0.3*op2 + 1.2*op3 - -state = randoperator(b_r, b_r) -result_ = randoperator(b_l, b_r) -result = deepcopy(result_) -operators.gemm!(complex(1.), op, state, complex(0.), result) -@test 1e-12 > D(result, op_*state) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, op, state, beta, result) -@test 1e-12 > D(result, alpha*op_*state + beta*result_) - -state = randoperator(b_l, b_l) -result_ = randoperator(b_l, b_r) -result = deepcopy(result_) -operators.gemm!(complex(1.), state, op, complex(0.), result) -@test 1e-12 > D(result, state*op_) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, state, op, beta, result) -@test 1e-12 > D(result, alpha*state*op_ + beta*result_) - -end # testset diff --git a/test/test_operators_lazytensor.jl b/test/test_operators_lazytensor.jl deleted file mode 100644 index 465c6ab3..00000000 --- a/test/test_operators_lazytensor.jl +++ /dev/null @@ -1,264 +0,0 @@ -using Base.Test -using QuantumOptics - -mutable struct test_lazytensor <: Operator - basis_l::Basis - basis_r::Basis - data::Matrix{Complex128} - test_lazytensor(b1::Basis, b2::Basis, data) = length(b1) == size(data, 1) && length(b2) == size(data, 2) ? new(b1, b2, data) : throw(DimensionMismatch()) -end - -@testset "operators-lazytensor" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) -D(x1::StateVector, x2::StateVector) = norm(x2-x1) - -b1a = GenericBasis(2) -b1b = GenericBasis(3) -b2a = GenericBasis(1) -b2b = GenericBasis(4) -b3a = GenericBasis(6) -b3b = GenericBasis(5) - -b_l = b1a⊗b2a⊗b3a -b_r = b1b⊗b2b⊗b3b - -op1 = randoperator(b1a, b1b) -op2 = randoperator(b2a, b2b) -op3 = randoperator(b3a, b3b) - -# Test creation -@test_throws AssertionError LazyTensor(b_l, b_r, [1], [randoperator(b1a)]) -@test_throws AssertionError LazyTensor(b_l, b_r, [1, 2], [op1]) -@test_throws AssertionError LazyTensor(b_l, b_r, [1, 2], [op1, sparse(randoperator(b_l, b_l))]) -@test_throws AssertionError LazyTensor(b_l, b_r, [1, 2], [randoperator(b_r, b_r), sparse(op2)]) - -@test LazyTensor(b_l, b_r, [2, 1], [op2, op1]) == LazyTensor(b_l, b_r, [1, 2], [op1, op2]) -x = randoperator(b2a) -@test LazyTensor(b_l, 2, x) == LazyTensor(b_l, b_l, [2], [x]) - -# Test copy -x = 2*LazyTensor(b_l, b_r, [1,2], [randoperator(b1a, b1b), sparse(randoperator(b2a, b2b))]) -x_ = copy(x) -@test x == x_ -@test !(x === x_) -x_.operators[1].data[1,1] = complex(10.) -@test x.operators[1].data[1,1] != x_.operators[1].data[1,1] -x_.factor = 3. -@test x_.factor != x.factor -x_.indices[2] = 100 -@test x_.indices != x.indices - - -# Test full & sparse -I2 = identityoperator(b2a, b2b) -x = LazyTensor(b_l, b_r, [1, 3], [op1, sparse(op3)], 0.3) -@test 1e-12 > D(0.3*op1⊗full(I2)⊗op3, full(x)) -@test 1e-12 > D(0.3*sparse(op1)⊗I2⊗sparse(op3), sparse(x)) - -# Test suboperators -@test operators_lazytensor.suboperator(x, 1) == op1 -@test operators_lazytensor.suboperator(x, 3) == sparse(op3) -@test operators_lazytensor.suboperators(x, [1, 3]) == [op1, sparse(op3)] - - -# Arithmetic operations -# ===================== -subop1 = randoperator(b1a, b1b) -subop2 = randoperator(b2a, b2b) -subop3 = randoperator(b3a, b3b) -I1 = full(identityoperator(b1a, b1b)) -I2 = full(identityoperator(b2a, b2b)) -I3 = full(identityoperator(b3a, b3b)) -op1 = LazyTensor(b_l, b_r, [1, 3], [subop1, sparse(subop3)], 0.1) -op1_ = 0.1*subop1 ⊗ I2 ⊗ subop3 -op2 = LazyTensor(b_l, b_r, [2, 3], [sparse(subop2), subop3], 0.7) -op2_ = 0.7*I1 ⊗ subop2 ⊗ subop3 -op3 = 0.3*LazyTensor(b_l, b_r, 3, subop3) -op3_ = 0.3*I1 ⊗ I2 ⊗ subop3 - -x1 = Ket(b_r, rand(Complex128, length(b_r))) -x2 = Ket(b_r, rand(Complex128, length(b_r))) -xbra1 = Bra(b_l, rand(Complex128, length(b_l))) -xbra2 = Bra(b_l, rand(Complex128, length(b_l))) - -# Addition -@test_throws ArgumentError op1 + op2 -@test_throws ArgumentError op1 - op2 -@test 1e-14 > D(-op1_, -op1) - -# Test multiplication -@test_throws bases.IncompatibleBases op1*op2 -@test 1e-11 > D(op1*(x1 + 0.3*x2), op1_*(x1 + 0.3*x2)) -@test 1e-11 > D((xbra1 + 0.3*xbra2)*op1, (xbra1 + 0.3*xbra2)*op1_) -@test 1e-11 > D(op1*x1 + 0.3*op1*x2, op1_*x1 + 0.3*op1_*x2) -@test 1e-12 > D(dagger(x1)*dagger(0.3*op2), dagger(x1)*dagger(0.3*op2_)) -@test 1e-12 > D(op1_*dagger(0.3*op2), op1_*dagger(0.3*op2_)) -@test 1e-12 > D(dagger(0.3*op2)*op1_, dagger(0.3*op2_)*op1_) -@test 1e-12 > D(dagger(0.3*op2)*op1, dagger(0.3*op2_)*op1_) - - -# Test division -@test 1e-14 > D(op1/7, op1_/7) - -# Test identityoperator -Idense = identityoperator(DenseOperator, b_r) -I = identityoperator(LazyTensor, b_r) -@test isa(I, LazyTensor) -@test full(I) == Idense -@test 1e-11 > D(I*x1, x1) -@test I == identityoperator(LazyTensor, b1b) ⊗ identityoperator(LazyTensor, b2b) ⊗ identityoperator(LazyTensor, b3b) - -Idense = identityoperator(DenseOperator, b_l) -I = identityoperator(LazyTensor, b_l) -@test isa(I, LazyTensor) -@test full(I) == Idense -@test 1e-11 > D(xbra1*I, xbra1) -@test I == identityoperator(LazyTensor, b1a) ⊗ identityoperator(LazyTensor, b2a) ⊗ identityoperator(LazyTensor, b3a) - - -# Test trace and normalize -subop1 = randoperator(b1a) -I2 = full(identityoperator(b2a)) -subop3 = randoperator(b3a) -op = LazyTensor(b_l, b_l, [1, 3], [subop1, sparse(subop3)], 0.1) -op_ = 0.1*subop1 ⊗ I2 ⊗ subop3 - -@test trace(op) ≈ trace(op_) -op_normalized = normalize(op) -@test trace(op_) ≈ trace(op) -@test 1 ≈ trace(op_normalized) -op_copy = deepcopy(op) -normalize!(op_copy) -@test trace(op) != trace(op_copy) -@test 1 ≈ trace(op_copy) - -# Test partial trace -subop1 = randoperator(b1a) -I2 = full(identityoperator(b2a)) -subop3 = randoperator(b3a) -op = LazyTensor(b_l, b_l, [1, 3], [subop1, sparse(subop3)], 0.1) -op_ = 0.1*subop1 ⊗ I2 ⊗ subop3 - -@test 1e-14 > D(ptrace(op_, 3), ptrace(op, 3)) -@test 1e-14 > D(ptrace(op_, 2), ptrace(op, 2)) -@test 1e-14 > D(ptrace(op_, 1), ptrace(op, 1)) - -@test 1e-14 > D(ptrace(op_, [2,3]), ptrace(op, [2,3])) -@test 1e-14 > D(ptrace(op_, [1,3]), ptrace(op, [1,3])) -@test 1e-14 > D(ptrace(op_, [1,2]), ptrace(op, [1,2])) - -@test_throws ArgumentError ptrace(op, [1,2,3]) - -# Test expect -state = Ket(b_l, rand(Complex128, length(b_l))) -@test expect(op, state) ≈ expect(op_, state) - -state = DenseOperator(b_l, b_l, rand(Complex128, length(b_l), length(b_l))) -@test expect(op, state) ≈ expect(op_, state) - -# Permute systems -subop1 = randoperator(b1a, b1b) -subop2 = randoperator(b2a, b2b) -subop3 = randoperator(b3a, b3b) -I2 = full(identityoperator(b2a, b2b)) -op = LazyTensor(b_l, b_r, [1, 3], [subop1, sparse(subop3)])*0.1 -op_ = 0.1*subop1 ⊗ I2 ⊗ subop3 - -@test 1e-14 > D(permutesystems(op, [1, 3, 2]), permutesystems(op_, [1, 3, 2])) -@test 1e-14 > D(permutesystems(op, [2, 1, 3]), permutesystems(op_, [2, 1, 3])) -@test 1e-14 > D(permutesystems(op, [2, 3, 1]), permutesystems(op_, [2, 3, 1])) -@test 1e-14 > D(permutesystems(op, [3, 1, 2]), permutesystems(op_, [3, 1, 2])) -@test 1e-14 > D(permutesystems(op, [3, 2, 1]), permutesystems(op_, [3, 2, 1])) - - -# Test gemv -subop1 = randoperator(b1a, b1b) -subop2 = randoperator(b2a, b2b) -subop3 = randoperator(b3a, b3b) -I2 = full(identityoperator(b2a, b2b)) -op = LazyTensor(b_l, b_r, [1, 3], [subop1, sparse(subop3)])*0.1 -op_ = 0.1*subop1 ⊗ I2 ⊗ subop3 - -state = Ket(b_r, rand(Complex128, length(b_r))) -result_ = Ket(b_l, rand(Complex128, length(b_l))) -result = deepcopy(result_) -operators.gemv!(complex(1.), op, state, complex(0.), result) -@test 1e-13 > D(result, op_*state) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemv!(alpha, op, state, beta, result) -@test 1e-13 > D(result, alpha*op_*state + beta*result_) - -state = Bra(b_l, rand(Complex128, length(b_l))) -result_ = Bra(b_r, rand(Complex128, length(b_r))) -result = deepcopy(result_) -operators.gemv!(complex(1.), state, op, complex(0.), result) -@test 1e-13 > D(result, state*op_) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemv!(alpha, state, op, beta, result) -@test 1e-13 > D(result, alpha*state*op_ + beta*result_) - -# Test gemm -b_l2 = GenericBasis(17) -b_r2 = GenericBasis(13) -subop1 = randoperator(b1a, b1b) -subop2 = randoperator(b2a, b2b) -subop3 = randoperator(b3a, b3b) -I2 = full(identityoperator(b2a, b2b)) -op = LazyTensor(b_l, b_r, [1, 3], [subop1, sparse(subop3)])*0.1 -op_ = 0.1*subop1 ⊗ I2 ⊗ subop3 - -state = randoperator(b_r, b_r2) -result_ = randoperator(b_l, b_r2) -result = deepcopy(result_) -operators.gemm!(complex(1.), op, state, complex(0.), result) -@test 1e-12 > D(result, op_*state) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, op, state, beta, result) -@test 1e-12 > D(result, alpha*op_*state + beta*result_) - -state = randoperator(b_l2, b_l) -result_ = randoperator(b_l2, b_r) -result = deepcopy(result_) -operators.gemm!(complex(1.), state, op, complex(0.), result) -@test 1e-12 > D(result, state*op_) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, state, op, beta, result) -@test 1e-12 > D(result, alpha*state*op_ + beta*result_) - -# Test calling gemv with non-complex factors -state = Ket(b_r, rand(Complex128, length(b_r))) -result_ = Ket(b_l, rand(Complex128, length(b_l))) -result = deepcopy(result_) -operators.gemv!(1, op, state, 0, result) -@test 1e-13 > D(result, op_*state) - -state = Bra(b_l, rand(Complex128, length(b_l))) -result_ = Bra(b_r, rand(Complex128, length(b_r))) -result = deepcopy(result_) -operators.gemv!(1, state, op, 0, result) -@test 1e-13 > D(result, state*op_) - -# Test gemm errors -test_op = test_lazytensor(b1a, b1a, rand(2, 2)) -test_lazy = LazyTensor(tensor(b1a, b1a), [1, 2], [test_op, test_op]) -test_ket = Ket(tensor(b1a, b1a), rand(4)) - -@test_throws ArgumentError operators.gemv!(alpha, test_lazy, test_ket, beta, copy(test_ket)) -@test_throws ArgumentError operators.gemv!(alpha, dagger(test_ket), test_lazy, beta, copy(dagger(test_ket))) - -end # testset diff --git a/test/test_operators_sparse.jl b/test/test_operators_sparse.jl deleted file mode 100644 index cc90aa96..00000000 --- a/test/test_operators_sparse.jl +++ /dev/null @@ -1,340 +0,0 @@ -using Base.Test -using QuantumOptics - - -# Custom operator type for testing error msg -mutable struct TestOperator <: Operator; end - - -@testset "operators-sparse" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) -D(x1::StateVector, x2::StateVector) = norm(x2-x1) -sprandop(b1, b2) = sparse(randoperator(b1, b2)) -sprandop(b) = sprandop(b, b) - -b1a = GenericBasis(2) -b1b = GenericBasis(3) -b2a = GenericBasis(1) -b2b = GenericBasis(4) -b3a = GenericBasis(1) -b3b = GenericBasis(5) - -b_l = b1a⊗b2a⊗b3a -b_r = b1b⊗b2b⊗b3b - -# Test creation -@test_throws DimensionMismatch DenseOperator(b1a, spzeros(Complex128, 3, 2)) -@test_throws DimensionMismatch DenseOperator(b1a, b1b, spzeros(Complex128, 3, 2)) -op1 = SparseOperator(b1a, b1b, sparse([1 1 1; 1 1 1])) -op2 = sparse(DenseOperator(b1b, b1a, [1 1; 1 1; 1 1])) -@test op1 == dagger(op2) - -# Test copy -op1 = sparse(randoperator(b1a)) -op2 = copy(op1) -@test !(op1.data === op2.data) -op2.data[1,1] = complex(10.) -@test op1.data[1,1] != op2.data[1,1] - -# Arithmetic operations -# ===================== -op_zero = SparseOperator(b_l, b_r) -op1 = sprandop(b_l, b_r) -op2 = sprandop(b_l, b_r) -op3 = sprandop(b_l, b_r) -op1_ = full(op1) -op2_ = full(op2) -op3_ = full(op3) - -x1 = randstate(b_r) -x2 = randstate(b_r) - -xbra1 = dagger(randstate(b_l)) -xbra2 = dagger(randstate(b_l)) - -# Addition -@test_throws bases.IncompatibleBases op1 + dagger(op2) -@test 1e-14 > D(op1+op2, op1_+op2_) -@test 1e-14 > D(op1+op2, op1+op2_) -@test 1e-14 > D(op1+op2, op1_+op2) - -# Subtraction -@test_throws bases.IncompatibleBases op1 - dagger(op2) -@test 1e-14 > D(op1-op2, op1_-op2_) -@test 1e-14 > D(op1-op2, op1-op2_) -@test 1e-14 > D(op1-op2, op1_-op2) -@test 1e-14 > D(op1+(-op2), op1_ - op2_) -@test 1e-14 > D(op1+(-1*op2), op1_ - op2_) - -# Test multiplication -@test_throws bases.IncompatibleBases op1*op2 -@test 1e-11 > D(op1*(x1 + 0.3*x2), op1_*(x1 + 0.3*x2)) -@test 1e-11 > D(op1*x1 + 0.3*op1*x2, op1_*x1 + 0.3*op1_*x2) -@test 1e-11 > D((op1+op2)*(x1+0.3*x2), (op1_+op2_)*(x1+0.3*x2)) - -@test 1e-11 > D((xbra1 + 0.3*xbra2)*op1, (xbra1 + 0.3*xbra2)*op1_) -@test 1e-11 > D(xbra1*op1 + 0.3*xbra2*op1, xbra1*op1_ + 0.3*xbra2*op1_) -@test 1e-11 > D((xbra1+0.3*xbra2)*(op1+op2), (xbra1+0.3*xbra2)*(op1_+op2_)) - -@test 1e-12 > D(op1*dagger(0.3*op2), op1_*dagger(0.3*op2_)) -@test 1e-12 > D(0.3*dagger(op2*dagger(op1)), 0.3*dagger(op2_*dagger(op1_))) -@test 1e-12 > D((op1 + op2)*dagger(0.3*op3), (op1_ + op2_)*dagger(0.3*op3_)) -@test 1e-12 > D(0.3*op1*dagger(op3) + 0.3*op2*dagger(op3), 0.3*op1_*dagger(op3_) + 0.3*op2_*dagger(op3_)) - -# Test division -@test 1e-14 > D(op1/7, op1_/7) - -# Conjugation -tmp = copy(op1) -conj!(tmp) -@test tmp == conj(op1) && conj(tmp.data) == op1.data - -# Test identityoperator -Idense = identityoperator(DenseOperator, b_r) -I = identityoperator(SparseOperator, b_r) -@test isa(I, SparseOperator) -@test full(I) == Idense -@test 1e-11 > D(I*x1, x1) -@test I == identityoperator(SparseOperator, b1b) ⊗ identityoperator(SparseOperator, b2b) ⊗ identityoperator(SparseOperator, b3b) - -Idense = identityoperator(DenseOperator, b_l) -I = identityoperator(SparseOperator, b_l) -@test isa(I, SparseOperator) -@test full(I) == Idense -@test 1e-11 > D(xbra1*I, xbra1) -@test I == identityoperator(SparseOperator, b1a) ⊗ identityoperator(SparseOperator, b2a) ⊗ identityoperator(SparseOperator, b3a) - - -# Test trace and normalize -op = sparse(DenseOperator(GenericBasis(3), [1 3 2;5 2 2;-1 2 5])) -@test 8 == trace(op) -op_normalized = normalize(op) -@test 8 == trace(op) -@test 1 == trace(op_normalized) -# op_ = normalize!(op) -# @test op_ === op -# @test 1 == trace(op) - -# Test partial trace -b1 = GenericBasis(3) -b2 = GenericBasis(5) -b3 = GenericBasis(7) -b_l = b1 ⊗ b2 ⊗ b3 -op1 = sprandop(b1) -op2 = sprandop(b2) -op3 = sprandop(b3) -op123 = op1 ⊗ op2 ⊗ op3 -op123_ = full(op123) - -@test 1e-14 > D(ptrace(op123_, 3), ptrace(op123, 3)) -@test 1e-14 > D(ptrace(op123_, 2), ptrace(op123, 2)) -@test 1e-14 > D(ptrace(op123_, 1), ptrace(op123, 1)) - -@test 1e-14 > D(ptrace(op123_, [2,3]), ptrace(op123, [2,3])) -@test 1e-14 > D(ptrace(op123_, [1,3]), ptrace(op123, [1,3])) -@test 1e-14 > D(ptrace(op123_, [1,2]), ptrace(op123, [1,2])) - -@test_throws ArgumentError ptrace(op123, [1,2,3]) - -# Test expect -state = randstate(b_l) -@test expect(op123, state) ≈ expect(op123_, state) - -state = randoperator(b_l) -@test expect(op123, state) ≈ expect(op123_, state) - - -# Tensor product -# ============== -b1a = GenericBasis(2) -b1b = GenericBasis(3) -b2a = GenericBasis(1) -b2b = GenericBasis(4) -b3a = GenericBasis(1) -b3b = GenericBasis(5) -b_l = b1a ⊗ b2a ⊗ b3a -b_r = b1b ⊗ b2b ⊗ b3b -op1a = sprandop(b1a, b1b) -op1b = sprandop(b1a, b1b) -op2a = sprandop(b2a, b2b) -op2b = sprandop(b2a, b2b) -op3a = sprandop(b3a, b3b) -op1a_ = full(op1a) -op1b_ = full(op1b) -op2a_ = full(op2a) -op2b_ = full(op2b) -op3a_ = full(op3a) -op123 = op1a ⊗ op2a ⊗ op3a -op123_ = op1a_ ⊗ op2a_ ⊗ op3a_ -@test op123.basis_l == b_l -@test op123.basis_r == b_r - -# Associativity -@test 1e-13 > D((op1a ⊗ op2a) ⊗ op3a, (op1a_ ⊗ op2a_) ⊗ op3a_) -@test 1e-13 > D(op1a ⊗ (op2a ⊗ op3a), op1a_ ⊗ (op2a_ ⊗ op3a_)) -@test 1e-13 > D(op1a ⊗ (op2a ⊗ op3a), op1a_ ⊗ (op2a_ ⊗ op3a)) - -# Linearity -@test 1e-13 > D(op1a ⊗ (0.3*op2a), op1a_ ⊗ (0.3*op2a_)) -@test 1e-13 > D(0.3*(op1a ⊗ op2a), 0.3*(op1a_ ⊗ op2a_)) -@test 1e-13 > D((0.3*op1a) ⊗ op2a, (0.3*op1a_) ⊗ op2a_) -@test 1e-13 > D(0.3*(op1a ⊗ op2a), 0.3*(op1a_ ⊗ op2a_)) -@test 1e-13 > D(0.3*(op1a ⊗ op2a), 0.3*(op1a ⊗ op2a_)) - -# Distributivity -@test 1e-13 > D(op1a ⊗ (op2a + op2b), op1a_ ⊗ (op2a_ + op2b_)) -@test 1e-13 > D(op1a ⊗ op2a + op1a ⊗ op2b, op1a_ ⊗ op2a_ + op1a_ ⊗ op2b_) -@test 1e-13 > D((op2a + op2b) ⊗ op3a, (op2a_ + op2b_) ⊗ op3a_) -@test 1e-13 > D(op2a ⊗ op3a + op2b ⊗ op3a, op2a_ ⊗ op3a_ + op2b_ ⊗ op3a_) -@test 1e-13 > D(op2a ⊗ op3a + op2b ⊗ op3a, op2a ⊗ op3a_ + op2b_ ⊗ op3a_) - -# Mixed-product property -@test 1e-13 > D((op1a ⊗ op2a) * dagger(op1b ⊗ op2b), (op1a_ ⊗ op2a_) * dagger(op1b_ ⊗ op2b_)) -@test 1e-13 > D((op1a*dagger(op1b)) ⊗ (op2a*dagger(op2b)), (op1a_*dagger(op1b_)) ⊗ (op2a_*dagger(op2b_))) -@test 1e-13 > D((op1a*dagger(op1b)) ⊗ (op2a*dagger(op2b)), (op1a_*dagger(op1b)) ⊗ (op2a_*dagger(op2b_))) - -# Transpose -@test 1e-13 > D(dagger(op1a ⊗ op2a), dagger(op1a_ ⊗ op2a_)) -@test 1e-13 > D(dagger(op1a ⊗ op2a), dagger(op1a ⊗ op2a_)) -@test 1e-13 > D(dagger(op1a) ⊗ dagger(op2a), dagger(op1a_) ⊗ dagger(op2a_)) - - -# Permute systems -op1 = sprandop(b1a, b1b) -op2 = sprandop(b2a, b2b) -op3 = sprandop(b3a, b3b) -op123 = op1⊗op2⊗op3 - -op132 = op1⊗op3⊗op2 -@test 1e-14 > D(permutesystems(op123, [1, 3, 2]), op132) - -op213 = op2⊗op1⊗op3 -@test 1e-14 > D(permutesystems(op123, [2, 1, 3]), op213) - -op231 = op2⊗op3⊗op1 -@test 1e-14 > D(permutesystems(op123, [2, 3, 1]), op231) - -op312 = op3⊗op1⊗op2 -@test 1e-14 > D(permutesystems(op123, [3, 1, 2]), op312) - -op321 = op3⊗op2⊗op1 -@test 1e-14 > D(permutesystems(op123, [3, 2, 1]), op321) - -# Test diagonaloperator -b = GenericBasis(4) -I = identityoperator(b) - -@test diagonaloperator(b, [1, 1, 1, 1]) == I -@test diagonaloperator(b, [1., 1., 1., 1.]) == I -@test diagonaloperator(b, [1im, 1im, 1im, 1im]) == 1im*I -@test diagonaloperator(b, [0:3;]) == sparse(DenseOperator(b, diagm([0:3;]))) - -# Test gemv -op = sprandop(b_l, b_r) -op_ = full(op) -xket = randstate(b_l) -xbra = dagger(xket) - -state = randstate(b_r) -result_ = randstate(b_l) -result = deepcopy(result_) -operators.gemv!(complex(1.0), op, state, complex(0.), result) -@test 1e-13 > D(result, op_*state) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemv!(alpha, op, state, beta, result) -@test 1e-13 > D(result, alpha*op_*state + beta*result_) - -state = dagger(randstate(b_l)) -result_ = dagger(randstate(b_r)) -result = deepcopy(result_) -operators.gemv!(complex(1.0), state, op, complex(0.), result) -@test 1e-13 > D(result, state*op_) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemv!(alpha, state, op, beta, result) -@test 1e-13 > D(result, alpha*state*op_ + beta*result_) - -# Test gemm small version -b1 = GenericBasis(3) -b2 = GenericBasis(5) -b3 = GenericBasis(7) - -op = sprandop(b1, b2) -op_ = full(op) - -state = randoperator(b2, b3) -result_ = randoperator(b1, b3) -result = deepcopy(result_) -operators.gemm!(complex(1.), op, state, complex(0.), result) -@test 1e-12 > D(result, op_*state) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, op, state, beta, result) -@test 1e-12 > D(result, alpha*op_*state + beta*result_) - -state = randoperator(b3, b1) -result_ = randoperator(b3, b2) -result = deepcopy(result_) -operators.gemm!(complex(1.), state, op, complex(0.), result) -@test 1e-12 > D(result, state*op_) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, state, op, beta, result) -@test 1e-12 > D(result, alpha*state*op_ + beta*result_) - -# Test gemm big version -b1 = GenericBasis(50) -b2 = GenericBasis(60) -b3 = GenericBasis(55) - -op = sprandop(b1, b2) -op_ = full(op) - -state = randoperator(b2, b3) -result_ = randoperator(b1, b3) -result = deepcopy(result_) -operators.gemm!(complex(1.), op, state, complex(0.), result) -@test 1e-11 > D(result, op_*state) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, op, state, beta, result) -@test 1e-11 > D(result, alpha*op_*state + beta*result_) - -state = randoperator(b3, b1) -result_ = randoperator(b3, b2) -result = deepcopy(result_) -operators.gemm!(complex(1.), state, op, complex(0.), result) -@test 1e-11 > D(result, state*op_) - -result = deepcopy(result_) -alpha = complex(1.5) -beta = complex(2.1) -operators.gemm!(alpha, state, op, beta, result) -@test 1e-11 > D(result, alpha*state*op_ + beta*result_) - -# Test remaining uncovered code -@test_throws DimensionMismatch SparseOperator(b1, b2, zeros(10, 10)) -dat = sprandop(b1, b1).data -@test SparseOperator(b1, dat) == SparseOperator(b1, Matrix{Complex128}(dat)) - -@test_throws ArgumentError sparse(TestOperator()) - -@test 2*SparseOperator(b1, dat) == SparseOperator(b1, dat)*2 -@test copy(op1) == deepcopy(op1) - - -end # testset diff --git a/test/test_particle.jl b/test/test_particle.jl deleted file mode 100644 index f822dca7..00000000 --- a/test/test_particle.jl +++ /dev/null @@ -1,409 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "particle" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) - -N = 200 -xmin = -32.5 -xmax = 20.1 - -basis_position = PositionBasis(xmin, xmax, N) -basis_momentum = MomentumBasis(basis_position) - -b2 = PositionBasis(basis_momentum) -@test basis_position.xmax - basis_position.xmin ≈ b2.xmax - b2.xmin -@test basis_position.N == b2.N - -# Test Gaussian wave-packet in both bases -x0 = 5.1 -p0 = -3.2 -sigma = 1. -sigma_x = sigma/sqrt(2) -sigma_p = 1./(sigma*sqrt(2)) - -psi0_bx = gaussianstate(basis_position, x0, p0, sigma) -psi0_bp = gaussianstate(basis_momentum, x0, p0, sigma) - -@test 1 ≈ norm(psi0_bx) -@test 1 ≈ norm(psi0_bp) - -p_bx = momentum(basis_position) -x_bx = position(basis_position) - -@test 1e-10 > D(p_bx, transform(basis_position, basis_momentum)*full(momentum(basis_momentum))*transform(basis_momentum, basis_position)) - -p_bp = momentum(basis_momentum) -x_bp = position(basis_momentum) - -@test x0 ≈ expect(x_bx, psi0_bx) -@test 0.1 > abs(p0 - expect(p_bx, psi0_bx)) -@test p0 ≈ expect(p_bp, psi0_bp) -@test 0.1 > abs(x0 - expect(x_bp, psi0_bp)) - -@test 1e-13 > abs(variance(x_bx, psi0_bx) - sigma^2/2) -@test 1e-13 > abs(variance(x_bp, psi0_bp) - sigma^2/2) -@test 1e-13 > abs(variance(p_bx, psi0_bx) - 1/(2*sigma^2)) -@test 1e-13 > abs(variance(p_bp, psi0_bp) - 1/(2*sigma^2)) - -# Test potentialoperator -V(x) = x^2 -V_bx = potentialoperator(basis_position, V) -V_bp = potentialoperator(basis_momentum, V) - -@test expect(V_bp, psi0_bp) ≈ expect(V_bx, psi0_bx) - - -# Test FFT transformation -function transformation(b1::MomentumBasis, b2::PositionBasis, psi::Ket) - Lp = (b1.pmax - b1.pmin) - dx = particle.spacing(b2) - if b1.N != b2.N || abs(2*pi/dx - Lp)/Lp > 1e-12 - throw(IncompatibleBases()) - end - N = b1.N - psi_shifted = exp.(1im*b2.xmin*(particle.samplepoints(b1)-b1.pmin)).*psi.data - psi_fft = exp.(1im*b1.pmin*particle.samplepoints(b2)).*ifft(psi_shifted)*sqrt(N) - return Ket(b2, psi_fft) -end - -function transformation(b1::PositionBasis, b2::MomentumBasis, psi::Ket) - Lx = (b1.xmax - b1.xmin) - dp = particle.spacing(b2) - if b1.N != b2.N || abs(2*pi/dp - Lx)/Lx > 1e-12 - throw(IncompatibleBases()) - end - N = b1.N - psi_shifted = exp.(-1im*b2.pmin*(particle.samplepoints(b1)-b1.xmin)).*psi.data - psi_fft = exp.(-1im*b1.xmin*particle.samplepoints(b2)).*fft(psi_shifted)/sqrt(N) - return Ket(b2, psi_fft) -end - -psi0_bx_fft = transformation(basis_position, basis_momentum, psi0_bx) -psi0_bp_fft = transformation(basis_momentum, basis_position, psi0_bp) - -@test 0.1 > abs(x0 - expect(x_bp, psi0_bx_fft)) -@test p0 ≈ expect(p_bp, psi0_bx_fft) -@test 0.1 > abs(p0 - expect(p_bx, psi0_bp_fft)) -@test x0 ≈ expect(x_bx, psi0_bp_fft) - -@test 1e-12 > norm(psi0_bx_fft - psi0_bp) -@test 1e-12 > norm(psi0_bp_fft - psi0_bx) - - -Tpx = transform(basis_momentum, basis_position) -Txp = transform(basis_position, basis_momentum) -psi0_bx_fft = Tpx*psi0_bx -psi0_bp_fft = Txp*psi0_bp - -@test 0.1 > abs(x0 - expect(x_bp, psi0_bx_fft)) -@test p0 ≈ expect(p_bp, psi0_bx_fft) -@test 0.1 > abs(p0 - expect(p_bx, psi0_bp_fft)) -@test x0 ≈ expect(x_bx, psi0_bp_fft) - -@test 1e-12 > norm(psi0_bx_fft - psi0_bp) -@test 1e-12 > norm(psi0_bp_fft - psi0_bx) - -@test 1e-12 > norm(dagger(Tpx*psi0_bx) - dagger(psi0_bx)*dagger(Tpx)) -@test 1e-12 > norm(dagger(Txp*psi0_bp) - dagger(psi0_bp)*dagger(Txp)) - -# Test gemv! -psi_ = deepcopy(psi0_bp) -operators.gemv!(Complex(1.), Tpx, psi0_bx, Complex(0.), psi_) -@test 1e-12 > norm(psi_ - psi0_bp) -operators.gemv!(Complex(1.), Tpx, psi0_bx, Complex(1.), psi_) -@test 1e-12 > norm(psi_ - 2*psi0_bp) - -psi_ = deepcopy(psi0_bx) -operators.gemv!(Complex(1.), Txp, psi0_bp, Complex(0.), psi_) -@test 1e-12 > norm(psi_ - psi0_bx) -operators.gemv!(Complex(1.), Txp, psi0_bp, Complex(1.), psi_) -@test 1e-12 > norm(psi_ - 2*psi0_bx) - - -alpha = complex(3.2) -beta = complex(-1.2) -randdata1 = rand(Complex128, N) -randdata2 = rand(Complex128, N) - -state = Ket(basis_position, randdata1) -result_ = Ket(basis_momentum, copy(randdata2)) -result0 = alpha*full(Tpx)*state + beta*result_ -operators.gemv!(alpha, Tpx, state, beta, result_) -@test 1e-11 > norm(result0 - result_) - -state = Bra(basis_position, randdata1) -result_ = Bra(basis_momentum, copy(randdata2)) -result0 = alpha*state*full(Txp) + beta*result_ -operators.gemv!(alpha, state, Txp, beta, result_) -@test 1e-11 > norm(result0 - result_) - -state = Ket(basis_momentum, randdata1) -result_ = Ket(basis_position, copy(randdata2)) -result0 = alpha*full(Txp)*state + beta*result_ -operators.gemv!(alpha, Txp, state, beta, result_) -@test 1e-11 > norm(result0 - result_) - -state = Bra(basis_momentum, randdata1) -result_ = Bra(basis_position, copy(randdata2)) -result0 = alpha*state*full(Tpx) + beta*result_ -operators.gemv!(alpha, state, Tpx, beta, result_) -@test 1e-11 > norm(result0 - result_) - - -# Test gemm! -rho0_xx = tensor(psi0_bx, dagger(psi0_bx)) -rho0_xp = tensor(psi0_bx, dagger(psi0_bp)) -rho0_px = tensor(psi0_bp, dagger(psi0_bx)) -rho0_pp = tensor(psi0_bp, dagger(psi0_bp)) - -rho_ = DenseOperator(basis_momentum, basis_position) -operators.gemm!(Complex(1.), Tpx, rho0_xx, Complex(0.), rho_) -@test 1e-12 > D(rho_, rho0_px) -@test 1e-12 > D(Tpx*rho0_xx, rho0_px) - -rho_ = DenseOperator(basis_position, basis_momentum) -operators.gemm!(Complex(1.), rho0_xx, Txp, Complex(0.), rho_) -@test 1e-12 > D(rho_, rho0_xp) -@test 1e-12 > D(rho0_xx*Txp, rho0_xp) - -rho_ = DenseOperator(basis_momentum, basis_momentum) -operators.gemm!(Complex(1.), Tpx, rho0_xp, Complex(0.), rho_) -@test 1e-12 > D(rho_, rho0_pp) -@test 1e-12 > D(Tpx*rho0_xx*Txp, rho0_pp) - -rho_ = DenseOperator(basis_momentum, basis_momentum) -operators.gemm!(Complex(1.), rho0_px, Txp, Complex(0.), rho_) -@test 1e-12 > D(rho_, rho0_pp) -@test 1e-12 > D(Txp*rho0_pp*Tpx, rho0_xx) - - -alpha = complex(3.2) -beta = complex(-1.2) -randdata1 = rand(Complex128, N, N) -randdata2 = rand(Complex128, N, N) - -op = DenseOperator(basis_position, basis_position, randdata1) -result_ = DenseOperator(basis_momentum, basis_position, copy(randdata2)) -result0 = alpha*full(Tpx)*op + beta*result_ -operators.gemm!(alpha, Tpx, op, beta, result_) -@test 1e-11 > D(result0, result_) - -result_ = DenseOperator(basis_position, basis_momentum, copy(randdata2)) -result0 = alpha*op*full(Txp) + beta*result_ -operators.gemm!(alpha, op, Txp, beta, result_) -@test 1e-11 > D(result0, result_) - -op = DenseOperator(basis_momentum, basis_momentum, randdata1) -result_ = DenseOperator(basis_position, basis_momentum, copy(randdata2)) -result0 = alpha*full(Txp)*op + beta*result_ -operators.gemm!(alpha, Txp, op, beta, result_) -@test 1e-11 > D(result0, result_) - -result_ = DenseOperator(basis_momentum, basis_position, copy(randdata2)) -result0 = alpha*op*full(Tpx) + beta*result_ -operators.gemm!(alpha, op, Tpx, beta, result_) -@test 1e-11 > D(result0, result_) - - - -# Test FFT with lazy product -psi_ = deepcopy(psi0_bx) -operators.gemv!(Complex(1.), LazyProduct(Txp, Tpx), psi0_bx, Complex(0.), psi_) -@test 1e-12 > norm(psi_ - psi0_bx) -@test 1e-12 > norm(Txp*(Tpx*psi0_bx) - psi0_bx) - -psi_ = deepcopy(psi0_bx) -I = full(identityoperator(basis_momentum)) -operators.gemv!(Complex(1.), LazyProduct(Txp, I, Tpx), psi0_bx, Complex(0.), psi_) -@test 1e-12 > norm(psi_ - psi0_bx) -@test 1e-12 > norm(Txp*I*(Tpx*psi0_bx) - psi0_bx) - -# Test dense FFT operator -Txp_dense = DenseOperator(Txp) -Tpx_dense = DenseOperator(Tpx) -@test isa(Txp_dense, DenseOperator) -@test isa(Tpx_dense, DenseOperator) -@test 1e-5 > D(Txp_dense*rho0_pp*Tpx_dense, rho0_xx) - -# Test FFT in 2D -N = [21, 18] -xmin = [-32.5, -10π] -xmax = [24.1, 9π] - -basis_position = [PositionBasis(xmin[i], xmax[i], N[i]) for i=1:2] -basis_momentum = MomentumBasis.(basis_position) - -x0 = [5.1, -0.2] -p0 = [-3.2, 1.33] -sigma = [1., 0.9] -sigma_x = sigma./sqrt(2) -sigma_p = 1./(sigma.*sqrt(2)) - -Txp = transform(tensor(basis_position...), tensor(basis_momentum...)) -Tpx = transform(tensor(basis_momentum...), tensor(basis_position...)) - -Txp_sub = [transform(basis_position[i], basis_momentum[i]) for i=1:2] -Tpx_sub = dagger.(Txp_sub) -Txp_full = full.(Txp_sub) -Txp_comp = tensor(Txp_full...) - -difference = (full(Txp) - Txp_comp).data -@test isapprox(difference, zeros(difference); atol=1e-12) - -psi0_x = gaussianstate.(basis_position, x0, p0, sigma_x) -psi0_p = gaussianstate.(basis_momentum, x0, p0, sigma_p) - -psi_p_fft = Tpx*tensor(psi0_x...) -psi_p_fft2 = tensor((Tpx_sub.*psi0_x)...) -@test norm(psi_p_fft - psi_p_fft2) < 1e-15 - -psi_x_fft = Txp*tensor(psi0_p...) -psi_x_fft2 = tensor((Txp_sub.*psi0_p)...) -@test norm(psi_p_fft - psi_p_fft2) < 1e-15 - -psi_p_fft = dagger(tensor(psi0_x...))*Txp -psi_p_fft2 = tensor((dagger.(psi0_x).*Txp_sub)...) -@test norm(psi_p_fft - psi_p_fft2) < 1e-15 - -psi_x_fft = dagger(tensor(psi0_p...))*Tpx -psi_x_fft2 = tensor((dagger.(psi0_p).*Tpx_sub)...) -@test norm(psi_p_fft - psi_p_fft2) < 1e-15 - -difference = (full(Txp) - identityoperator(DenseOperator, Txp.basis_l)*Txp).data -@test isapprox(difference, zeros(difference); atol=1e-12) -@test_throws AssertionError transform(tensor(basis_position...), tensor(basis_position...)) -@test_throws particle.IncompatibleBases transform(SpinBasis(1//2)^2, SpinBasis(1//2)^2) - -@test full(Txp) == full(Txp_sub[1] ⊗ Txp_sub[2]) - -# Test ket only FFTs -Txp = transform(tensor(basis_position...), tensor(basis_momentum...); ket_only=true) -Tpx = transform(tensor(basis_momentum...), tensor(basis_position...); ket_only=true) - -Txp_sub = [transform(basis_position[i], basis_momentum[i]; ket_only=true) for i=1:2] -Tpx_sub = dagger.(Txp_sub) - -psi_p_fft = Tpx*tensor(psi0_x...) -psi_p_fft2 = tensor((Tpx_sub.*psi0_x)...) -@test norm(psi_p_fft - psi_p_fft2) < 1e-15 - -psi_x_fft = Txp*tensor(psi0_p...) -psi_x_fft2 = tensor((Txp_sub.*psi0_p)...) -@test norm(psi_p_fft - psi_p_fft2) < 1e-15 - -psi_p_fft = dagger(tensor(psi0_x...))*Txp -psi_p_fft2 = tensor((dagger.(psi0_x).*Txp_sub)...) -@test norm(psi_p_fft - psi_p_fft2) < 1e-15 - -psi_x_fft = dagger(tensor(psi0_p...))*Tpx -psi_x_fft2 = tensor((dagger.(psi0_p).*Tpx_sub)...) -@test norm(psi_p_fft - psi_p_fft2) < 1e-15 - -psi_x_fft = Txp*tensor(psi0_p...) -psi_x_fft2 = tensor(Txp_sub...)*tensor(psi0_p...) -@test norm(psi_x_fft - psi_x_fft2) < 1e-15 - -# Test composite basis of mixed type -bc = FockBasis(2) -psi_fock = fockstate(FockBasis(2), 1) -psi1 = tensor(psi0_p[1], psi_fock, psi0_p[2]) -psi2 = tensor(psi0_x[1], psi_fock, psi0_x[2]) - -basis_l = tensor(basis_position[1], bc, basis_position[2]) -basis_r = tensor(basis_momentum[1], bc, basis_momentum[2]) -Txp = transform(basis_l, basis_r; ket_only=true) -Tpx = transform(basis_r, basis_l; ket_only=true) - -psi1_fft = Txp*psi1 -psi1_fft2 = tensor(Txp_sub[1]*psi0_p[1], psi_fock, Txp_sub[2]*psi0_p[2]) -@test norm(psi1_fft - psi1_fft2) < 1e-15 - -psi2_fft = Tpx*psi2 -psi2_fft2 = tensor(Tpx_sub[1]*psi0_x[1], psi_fock, Tpx_sub[2]*psi0_x[2]) -@test norm(psi2_fft - psi2_fft2) < 1e-15 - -Txp = transform(basis_l, basis_r) -Txp_sub = [transform(basis_position[i], basis_momentum[i]) for i=1:2] -difference = (full(Txp) - tensor(full(Txp_sub[1]), full(one(bc)), full(Txp_sub[2]))).data -@test isapprox(difference, zeros(difference); atol=1e-12) - -basis_l = tensor(bc, basis_position[1], basis_position[2]) -basis_r = tensor(bc, basis_momentum[1], basis_momentum[2]) -Txp2 = transform(basis_l, basis_r) -Tpx2 = transform(basis_r, basis_l) -difference = (full(Txp) - permutesystems(full(Txp2), [2, 1, 3])).data -@test isapprox(difference, zeros(difference); atol=1e-13) -difference = (full(dagger(Txp)) - permutesystems(full(Tpx2), [2, 1, 3])).data -@test isapprox(difference, zeros(difference); atol=1e-13) - -# Test potentialoperator in more than 1D -N = [21, 18] -xmin = [-32.5, -10π] -xmax = [24.1, 9π] - -basis_position = [PositionBasis(xmin[i], xmax[i], N[i]) for i=1:2] -basis_momentum = MomentumBasis.(basis_position) - -bcomp_pos = tensor(basis_position...) -bcomp_mom = tensor(basis_momentum...) -V(x, y) = sin(x*y) + cos(x) -xsample, ysample = samplepoints.(basis_position) -V_op = diagonaloperator(bcomp_pos, [V(x, y) for y in ysample for x in xsample]) -V_op2 = potentialoperator(bcomp_pos, V) -@test V_op == V_op2 - -basis_position = PositionBasis.(basis_momentum) -bcomp_pos = tensor(basis_position...) -Txp = transform(bcomp_pos, bcomp_mom) -Tpx = transform(bcomp_mom, bcomp_pos) -xsample, ysample = samplepoints.(basis_position) -V_op = Tpx*full(diagonaloperator(bcomp_pos, [V(x, y) for y in ysample for x in xsample]))*Txp -V_op2 = potentialoperator(bcomp_mom, V) -@test V_op == V_op2 - -N = [17, 12, 9] -xmin = [-32.5, -10π, -0.1] -xmax = [24.1, 9π, 22.0] - -basis_position = [PositionBasis(xmin[i], xmax[i], N[i]) for i=1:3] -basis_momentum = MomentumBasis.(basis_position) - -bcomp_pos = tensor(basis_position...) -bcomp_mom = tensor(basis_momentum...) -V(x, y, z) = exp(-z^2) + sin(x*y) + cos(x) -xsample, ysample, zsample = samplepoints.(basis_position) -V_op = diagonaloperator(bcomp_pos, [V(x, y, z) for z in zsample for y in ysample for x in xsample]) -V_op2 = potentialoperator(bcomp_pos, V) -@test V_op == V_op2 - -# Test error messages -b1 = PositionBasis(-1, 1, 50) -b2 = MomentumBasis(-1, 1, 30) -@test_throws bases.IncompatibleBases transform(b1, b2) -@test_throws bases.IncompatibleBases transform(b2, b1) - -bc1 = b1 ⊗ bc -bc2 = b2 ⊗ bc -@test_throws bases.IncompatibleBases transform(bc1, bc2) -@test_throws bases.IncompatibleBases transform(bc2, bc1) - -b1 = PositionBasis(-1, 1, 50) -b2 = MomentumBasis(-1, 1, 50) -bc1 = b1 ⊗ bc -bc2 = b2 ⊗ bc -@test_throws bases.IncompatibleBases transform(bc1, bc2) -@test_throws bases.IncompatibleBases transform(bc2, bc1) -@test_throws bases.IncompatibleBases transform(bc1, bc2; index=[2]) - -bc1 = b1 ⊗ b2 -bc2 = b1 ⊗ b2 -@test_throws bases.IncompatibleBases transform(bc1, bc2) -@test_throws bases.IncompatibleBases transform(bc2, bc1) - -@test_throws bases.IncompatibleBases potentialoperator(bc ⊗ bc, V) - -end # testset diff --git a/test/test_phasespace.jl b/test/test_phasespace.jl old mode 100755 new mode 100644 index 22b6f8a6..5c7606d3 --- a/test/test_phasespace.jl +++ b/test/test_phasespace.jl @@ -1,11 +1,12 @@ -using Base.Test +using Test using QuantumOptics +using Random, LinearAlgebra @testset "phasespace" begin -srand(0) +Random.seed!(0) -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) +D(op1::AbstractOperator, op2::AbstractOperator) = abs(tracedistance_nh(dense(op1), dense(op2))) # Test quasi-probability functions b = FockBasis(100) @@ -32,7 +33,7 @@ Wrho_fock = wigner(rho_fock, X, Y) laguerre3(x) = (-x^3+9x^2-18x+6)/6 for (i,x)=enumerate(X), (j,y)=enumerate(Y) - beta = 1./sqrt(2)*complex(x, y) + beta = 1.0/sqrt(2)*complex(x, y) betastate = coherentstate(b, beta) q_coherent = 1/pi*exp(-abs2(alpha-beta)) @@ -103,7 +104,7 @@ ssq = sx^2 + sy^2 + sz^2 qsu2sx = qfuncsu2(csssx,theta,phi) qsu2sxdm = qfuncsu2(dmcsssx,theta,phi) res = 250 -costhetam = Array{Float64}(res,2*res) +costhetam = Array{Float64}(undef,res,2*res) for i = 1:res, j = 1:2*res costhetam[i,j] = sin(i*1pi/(res-1)) end diff --git a/test/test_polynomials.jl b/test/test_polynomials.jl deleted file mode 100644 index 3ee9f8ef..00000000 --- a/test/test_polynomials.jl +++ /dev/null @@ -1,30 +0,0 @@ -using Base.Test -using QuantumOptics -using QuantumOptics.polynomials - - -@testset "polynomials" begin - -# Test Horner scheme -c = [0.2, 0.6, 1.7] -x0 = 1.3 -@test horner(c, x0) == c[1] + c[2]*x0 + c[3]*x0^2 - -# Test Hermite polynomials -an = Vector{Vector{Int}}(8) -an[1] = [1] -an[2] = [0,2] -an[3] = [-2, 0, 4] -an[4] = [0, -12, 0, 8] -an[5] = [12, 0, -48, 0, 16] -an[6] = [0, 120, 0, -160, 0, 32] -an[7] = [-120, 0, 720, 0, -480, 0, 64] -an[8] = [0, -1680, 0, 3360, 0, -1344, 0, 128] -@test hermite.a(7) == an - -A = hermite.A(7) -for n=0:7 - @test A[n+1] ≈ an[n+1]/sqrt(2^n*factorial(n)) -end - -end # testset diff --git a/test/test_printing.jl b/test/test_printing.jl deleted file mode 100644 index c2bb00d4..00000000 --- a/test/test_printing.jl +++ /dev/null @@ -1,149 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "printing" begin - -@test sprint(show, GenericBasis([2, 3])) == "Basis(shape=[2,3])" -@test sprint(show, GenericBasis(2)) == "Basis(dim=2)" -@test sprint(show, SpinBasis(1//1)) == "Spin(1)" -@test sprint(show, SpinBasis(3//2)) == "Spin(3/2)" -@test sprint(show, FockBasis(1)) == "Fock(cutoff=1)" -@test sprint(show, NLevelBasis(2)) == "NLevel(N=2)" - -@test sprint(show, PositionBasis(-4, 4, 10)) == "Position(xmin=-4.0, xmax=4.0, N=10)" -@test sprint(show, MomentumBasis(-4, 4, 10)) == "Momentum(pmin=-4.0, pmax=4.0, N=10)" - -b_fock = FockBasis(4) -states = [fockstate(b_fock, 2), coherentstate(b_fock, 0.4)] -@test sprint(show, SubspaceBasis(states)) == "Subspace(superbasis=Fock(cutoff=4), states:2)" - -b_mb = ManyBodyBasis(b_fock, fermionstates(b_fock, 2)) -@test sprint(show, b_mb) == "ManyBody(onebodybasis=Fock(cutoff=4), states:10)" - -state = fockstate(FockBasis(2), 2) -@test sprint(show, state) == "Ket(dim=3)\n basis: Fock(cutoff=2)\n 0.0+0.0im\n 0.0+0.0im\n 1.0+0.0im" -state = dagger(state) -@test sprint(show, state) == "Bra(dim=3)\n basis: Fock(cutoff=2)\n 0.0-0.0im\n 0.0-0.0im\n 1.0-0.0im" - -op = DenseOperator(FockBasis(1)) -@test sprint(show, op) == "DenseOperator(dim=2x2) - basis: Fock(cutoff=1) - 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im" - -op = DenseOperator(b_fock, b_fock ⊗ SpinBasis(1//2)) -@test sprint(show, op) == "DenseOperator(dim=5x10) - basis left: Fock(cutoff=4) - basis right: [Fock(cutoff=4) ⊗ Spin(1/2)] - 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im" - -op = SparseOperator(b_fock, b_fock ⊗ SpinBasis(1//2)) -@test sprint(show, op) == "SparseOperator(dim=5x10) - basis left: Fock(cutoff=4) - basis right: [Fock(cutoff=4) ⊗ Spin(1/2)] - []" - -op = SparseOperator(b_fock) -op.data[2,2] = 1 -@test replace(sprint(show, op), "\t", " ") == "SparseOperator(dim=5x5) - basis: Fock(cutoff=4)\n [2, 2] = 1.0+0.0im" - -op = LazySum(SparseOperator(b_fock), DenseOperator(b_fock)) -@test sprint(show, op) == "LazySum(dim=5x5) - basis: Fock(cutoff=4) - operators: 2" - -op = LazyProduct(SparseOperator(b_fock), DenseOperator(b_fock)) -@test sprint(show, op) == "LazyProduct(dim=5x5) - basis: Fock(cutoff=4) - operators: 2" - -b_fock = FockBasis(2) -b_spin = SpinBasis(1//2) -b_mb = ManyBodyBasis(b_spin, fermionstates(b_spin, 1)) -op = LazyTensor(b_fock ⊗ b_mb ⊗ b_spin, [1, 3], [SparseOperator(b_fock), DenseOperator(b_spin)]) -@test sprint(show, op) == "LazyTensor(dim=12x12) - basis: [Fock(cutoff=2) ⊗ ManyBody(onebodybasis=Spin(1/2), states:2) ⊗ Spin(1/2)] - operators: 2 - indices: [1,3]" - -bx = PositionBasis(-2, 2, 4) -bp = MomentumBasis(bx) -Tpx = transform(bp, bx) -@test sprint(show, Tpx) == "FFTOperators(dim=4x4) - basis left: Momentum(pmin=-3.141592653589793, pmax=3.141592653589793, N=4) - basis right: Position(xmin=-2.0, xmax=2.0, N=4)" - -# Inversed tensor product ordering -QuantumOptics.set_printing(standard_order=true) - -n = fockstate(b_fock, 1) -@test sprint(show, n) == "Ket(dim=3)\n basis: Fock(cutoff=2)\n 0.0+0.0im\n 1.0+0.0im\n 0.0+0.0im" - -spin1 = spindown(b_spin) -spin2 = spinup(b_spin) -state = n ⊗ spin1 ⊗ spin2 -state_data = kron(n.data, spin1.data, spin2.data) -type_len = length("Complex{Float64}") -state_data_str = join(split(sprint(show, state_data)[type_len+2:end-1], ','), "\n") -@test sprint(show, state) == "Ket(dim=12) - basis: [Fock(cutoff=2) ⊗ Spin(1/2) ⊗ Spin(1/2)]\n "*state_data_str - -state_data_str = join(split(sprint(show, state_data')[type_len+2:end-1]), "\n ") -@test sprint(show, dagger(state)) == "Bra(dim=12) - basis: [Fock(cutoff=2) ⊗ Spin(1/2) ⊗ Spin(1/2)]\n "*state_data_str - -op = dm(state) -op_data = state_data * state_data' -op_data_str1 = split(sprint(show, op_data)[type_len+2:end-1], ";") -for i=1:length(op_data_str1) - op_data_str1[i] = join(split(op_data_str1[i]), " ") -end -op_data_str = join(op_data_str1, "\n ") -@test sprint(show, op) == "DenseOperator(dim=12x12) - basis: [Fock(cutoff=2) ⊗ Spin(1/2) ⊗ Spin(1/2)]\n "*op_data_str - -op = sparse(op) -op_data = sparse(op_data) -op_data_str = sprint(show, op_data)[4:end] -@test sprint(show, op) == "SparseOperator(dim=12x12) - basis: [Fock(cutoff=2) ⊗ Spin(1/2) ⊗ Spin(1/2)]\n "*op_data_str - -paulix, pauliy = sigmax(b_spin), sigmay(b_spin) -pauli = paulix ⊗ pauliy -@test sprint(show, pauli) == "SparseOperator(dim=4x4)\n basis: [Spin(1/2) ⊗ Spin(1/2)]\n [4, 1] = 0.0+1.0im\n [3, 2] = 0.0-1.0im\n [2, 3] = 0.0+1.0im\n [1, 4] = 0.0-1.0im" -@test sprint((io, x) -> show(IOContext(io, :limit=>true, :displaysize=>(20,80)), x), pauli ⊗ pauli) == "SparseOperator(dim=16x16)\n basis: [Spin(1/2) ⊗ Spin(1/2) ⊗ Spin(1/2) ⊗ Spin(1/2)]\n [16, 1] = -1.0+0.0im\n [15, 2] = 1.0+0.0im\n [14, 3] = -1.0+0.0im\n [13, 4] = 1.0+0.0im\n [12, 5] = 1.0+0.0im\n ⋮\n [6 , 11] = -1.0+0.0im\n [5 , 12] = 1.0+0.0im\n [4 , 13] = 1.0+0.0im\n [3 , 14] = -1.0-0.0im\n [2 , 15] = 1.0+0.0im\n [1 , 16] = -1.0-0.0im" -@test sprint(show, full(pauli)) == "DenseOperator(dim=4x4) - basis: [Spin(1/2) ⊗ Spin(1/2)] - 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0-1.0im - 0.0+0.0im 0.0+0.0im 0.0+1.0im 0.0+0.0im - 0.0+0.0im 0.0-1.0im 0.0+0.0im 0.0+0.0im - 0.0+1.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im" - -hadamard = DenseOperator(b_spin, 1/sqrt(2) * [1 1; 1 -1]) -@test sprint(show, sigmax(b_spin) ⊗ hadamard) == "SparseOperator(dim=4x4)\n basis: [Spin(1/2) ⊗ Spin(1/2)]\n [3, 1] = 0.707107+0.0im\n [4, 1] = 0.707107+0.0im\n [3, 2] = 0.707107+0.0im\n [4, 2] = -0.707107+0.0im\n [1, 3] = 0.707107+0.0im\n [2, 3] = 0.707107+0.0im\n [1, 4] = 0.707107+0.0im\n [2, 4] = -0.707107+0.0im" -@test sprint((io, x) -> show(IOContext(io, :limit=>true, :displaysize=>(20,80)), x), full(sigmax(b_spin) ⊗ hadamard ⊗ hadamard ⊗ hadamard)) == -"DenseOperator(dim=16x16)\n basis: [Spin(1/2) ⊗ Spin(1/2) ⊗ Spin(1/2) ⊗ Spin(1/2)]\n 0.0+0.0im 0.0+0.0im … 0.353553+0.0im 0.353553+0.0im\n 0.0+0.0im 0.0+0.0im 0.353553+0.0im -0.353553+0.0im\n 0.0+0.0im 0.0+0.0im -0.353553+0.0im -0.353553+0.0im\n 0.0+0.0im 0.0+0.0im -0.353553+0.0im 0.353553-0.0im\n 0.0+0.0im 0.0+0.0im -0.353553+0.0im -0.353553+0.0im\n 0.0+0.0im 0.0+0.0im … -0.353553+0.0im 0.353553-0.0im\n 0.0+0.0im 0.0+0.0im 0.353553+0.0im 0.353553+0.0im\n 0.0+0.0im 0.0+0.0im 0.353553+0.0im -0.353553+0.0im\n 0.353553+0.0im 0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im -0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im 0.353553+0.0im … 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im -0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im 0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im -0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im 0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im -0.353553+0.0im … 0.0+0.0im 0.0+0.0im" - -@test sprint((io, x) -> show(IOContext(io, :limit=>true, :displaysize=>(16,80)), x), full(sigmax(b_spin) ⊗ hadamard ⊗ hadamard ⊗ hadamard)) == -"DenseOperator(dim=16x16)\n basis: [Spin(1/2) ⊗ Spin(1/2) ⊗ Spin(1/2) ⊗ Spin(1/2)]\n 0.0+0.0im 0.0+0.0im … 0.353553+0.0im 0.353553+0.0im\n 0.0+0.0im 0.0+0.0im 0.353553+0.0im -0.353553+0.0im\n 0.0+0.0im 0.0+0.0im -0.353553+0.0im -0.353553+0.0im\n 0.0+0.0im 0.0+0.0im -0.353553+0.0im 0.353553-0.0im\n 0.0+0.0im 0.0+0.0im -0.353553+0.0im -0.353553+0.0im\n 0.0+0.0im 0.0+0.0im … -0.353553+0.0im 0.353553-0.0im\n ⋮ ⋱ ⋮ \n 0.353553+0.0im -0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im 0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im -0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im 0.353553+0.0im 0.0+0.0im 0.0+0.0im\n 0.353553+0.0im -0.353553+0.0im … 0.0+0.0im 0.0+0.0im" - -nlevel1 = basisstate(NLevelBasis(4), 1) -nlevel2 = basisstate(NLevelBasis(4), 2) -nlevel3 = basisstate(NLevelBasis(3), 3) -@test sprint((io, x) -> show(IOContext(io, :limit=>true, :displaysize=>(16,80)), x), (nlevel1 ⊗ nlevel2) ⊗ dagger(nlevel3)) == -"DenseOperator(dim=16x3)\n basis left: [NLevel(N=4) ⊗ NLevel(N=4)]\n basis right: NLevel(N=3)\n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 0.0+0.0im 1.0+0.0im\n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n ⋮ \n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 0.0+0.0im 0.0+0.0im" - -# Test switching back -QuantumOptics.set_printing(standard_order=false) -state_data = kron(spin2.data, spin1.data, n.data) -state_data_str = join(split(sprint(show, state_data)[type_len+2:end-1], ','), "\n") -@test sprint(show, state) == "Ket(dim=12) - basis: [Fock(cutoff=2) ⊗ Spin(1/2) ⊗ Spin(1/2)]\n "*state_data_str - - -end # testset diff --git a/test/test_semiclassical.jl b/test/test_semiclassical.jl index 95236818..76e1378c 100644 --- a/test/test_semiclassical.jl +++ b/test/test_semiclassical.jl @@ -1,5 +1,6 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra @testset "semiclassical" begin @@ -41,7 +42,7 @@ x_rho = semiclassical.State(rho, [complex(1., 0.)]) @test variance(n, x_rho) ≈ abs2(alpha) @test variance(n, [x_ket, x_rho]) ≈ [abs2(alpha), abs2(alpha)] -# Test partial trace +# Test partial tr b1 = GenericBasis(3) b2 = GenericBasis(5) b = b1 ⊗ b2 @@ -58,7 +59,7 @@ x = semiclassical.State(rho, [0.4, -0.3im]) # Test dm function b = GenericBasis(4) psi = randstate(b) -u = Complex128[complex(2., 3.)] +u = ComplexF64[complex(2., 3.)] state = semiclassical.State(psi, u) @test dm(state) == semiclassical.State(dm(psi), u) @@ -66,17 +67,17 @@ state = semiclassical.State(psi, u) # Test casting between and semiclassical states and complex vectors b = GenericBasis(4) rho = randoperator(b) -u = rand(Complex128, 3) +u = rand(ComplexF64, 3) state = semiclassical.State(rho, u) -state_ = semiclassical.State(randoperator(b), rand(Complex128, 3)) -x = Vector{Complex128}(16+3) +state_ = semiclassical.State(randoperator(b), rand(ComplexF64, 3)) +x = Vector{ComplexF64}(undef, 19) semiclassical.recast!(state, x) semiclassical.recast!(x, state_) @test state_ == state # Test master -basis = SpinBasis(1//2) +spinbasis = SpinBasis(1//2) # Random 2 level Hamiltonian a1 = 0.5 @@ -85,18 +86,18 @@ c = 1.3 d = -4.7 data = [a1 c-1im*d; c+1im*d a2] -H = DenseOperator(basis, data) +H = DenseOperator(spinbasis, data) a = (a1 + a2)/2 b = (a1 - a2)/2 r = [c d b] -sigma_r = c*sigmax(basis) + d*sigmay(basis) + b*sigmaz(basis) +sigma_r = c*sigmax(spinbasis) + d*sigmay(spinbasis) + b*sigmaz(spinbasis) -U(t) = exp(-1im*a*t)*(cos(norm(r)*t)*one(basis) - 1im*sin(norm(r)*t)*sigma_r/norm(r)) +U(t) = exp(-1im*a*t)*(cos(norm(r)*t)*one(spinbasis) - 1im*sin(norm(r)*t)*sigma_r/norm(r)) # Random initial state -psi0 = randstate(basis) +psi0 = randstate(spinbasis) T = [0:0.5:1;] @@ -107,8 +108,8 @@ function fclassical(t, quantumstate, u, du) du[1] = -1*u[1] end -state0 = semiclassical.State(psi0, Complex128[complex(2., 3.)]) -function f(t, state::semiclassical.State{Ket}) +state0 = semiclassical.State(psi0, ComplexF64[complex(2., 3.)]) +function f(t, state::semiclassical.State{B,T}) where {B<:Basis,T<:Ket{B}} @test 1e-5 > norm(state.quantum - U(t)*psi0) @test 1e-5 > abs(state.classical[1] - state0.classical[1]*exp(-t)) end @@ -116,7 +117,7 @@ semiclassical.schroedinger_dynamic(T, state0, fquantum_schroedinger, fclassical; tout, state_t = semiclassical.schroedinger_dynamic(T, state0, fquantum_schroedinger, fclassical) f(T[end], state_t[end]) -function f(t, state::semiclassical.State{DenseOperator}) +function f(t, state::semiclassical.State{B,T}) where {B<:Basis,T<:DenseOperator{B,B}} @test 1e-5 > tracedistance(state.quantum, dm(U(t)*psi0)) @test 1e-5 > abs(state.classical[1] - state0.classical[1]*exp(-t)) end @@ -124,4 +125,54 @@ semiclassical.master_dynamic(T, state0, fquantum_master, fclassical; fout=f) tout, state_t = semiclassical.master_dynamic(T, state0, fquantum_master, fclassical) f(T[end], state_t[end]) -end # testset +# Test mcwf +# Set up system where only atom can jump once +ba = SpinBasis(1//2) +bf = FockBasis(5) +sm = sigmam(ba)⊗one(bf) +a = one(ba)⊗destroy(bf) +H = 0*sm +J = [0*a,sm] +Jdagger = dagger.(J) +function fquantum(t,psi,u) + return H, J, Jdagger +end +function fclassical(t,psi,u,du) + du[1] = u[2] # dx + du[2] = 0.0 +end +njumps = [0] +function fjump_classical(t,psi,u,i) + @test i==2 + njumps .+= 1 + u[2] += 1.0 +end +u0 = rand(2) .+ 0.0im +ψ0 = semiclassical.State(spinup(ba)⊗fockstate(bf,0),u0) + +tout1, ψt1 = semiclassical.mcwf_dynamic(T,ψ0,fquantum,fclassical,fjump_classical,seed=1) +@test njumps == [1] +tout2, ψt2 = semiclassical.mcwf_dynamic(T,ψ0,fquantum,fclassical,fjump_classical,seed=1) +@test ψt2 == ψt1 +tout3, ψt3 = semiclassical.mcwf_dynamic(T,ψ0,fquantum,fclassical,fjump_classical;display_beforeevent=true,seed=1) +@test length(ψt3) == length(ψt1)+1 +tout4, ψt4 = semiclassical.mcwf_dynamic(T,ψ0,fquantum,fclassical,fjump_classical;display_beforeevent=true,display_afterevent=true,seed=1) +@test length(ψt4) == length(ψt1)+2 +tout5, ut = semiclassical.mcwf_dynamic(T,ψ0,fquantum,fclassical,fjump_classical;display_beforeevent=true,display_afterevent=true,seed=1,fout=(t,psi)->copy(psi.classical)) + +@test ψt1[end].classical[2] == u0[2] + 1.0 + +# Test classical jump behavior +before_jump = findfirst(t -> !(t∈T), tout3) +after_jump = findlast(t-> !(t∈T), tout4) +@test after_jump == before_jump+1 +@test ψt3[before_jump].classical[2] == u0[2] +@test ψt4[after_jump].classical[2] == u0[2] + 1.0 +@test ut == [ψ.classical for ψ=ψt4] + +# Test quantum jumps +@test ψt1[end].quantum == spindown(ba)⊗fockstate(bf,0) +@test ψt4[before_jump].quantum == ψ0.quantum +@test ψt4[after_jump].quantum == spindown(ba)⊗fockstate(bf,0) + +end # testsets diff --git a/test/test_sortedindices.jl b/test/test_sortedindices.jl deleted file mode 100644 index f748f472..00000000 --- a/test/test_sortedindices.jl +++ /dev/null @@ -1,55 +0,0 @@ -using Base.Test -using QuantumOptics - - -@testset "sortedindices" begin - -s = QuantumOptics.sortedindices - -@test s.complement(6, [1, 4]) == [2, 3, 5, 6] - -@test s.union(Int[], Int[]) == Int[] -@test s.union(Int[], [2, 3]) == [2, 3] -@test s.union([2, 3], Int[]) == [2, 3] -@test s.union([1, 4, 5], [2, 4, 7]) == [1, 2, 4, 5, 7] -@test s.union([2, 4, 7], [1, 4, 5]) == [1, 2, 4, 5, 7] -@test s.union([1, 4, 5], [2, 4]) == [1, 2, 4, 5] -@test s.union([2, 4], [1, 4, 5]) == [1, 2, 4, 5] -@test s.union([1, 4, 5], [2, 4, 5]) == [1, 2, 4, 5] -@test s.union([2, 4, 5], [1, 4, 5]) == [1, 2, 4, 5] - -@test s.remove([1, 4, 5], [2, 4, 7]) == [1, 5] -@test s.remove([1, 4, 5, 7], [2, 4, 7]) == [1, 5] -@test s.remove([1, 4, 5, 8], [2, 4, 7]) == [1, 5, 8] - -@test s.shiftremove([1, 4, 5], [2, 4, 7]) == [1, 3] -@test s.shiftremove([1, 4, 5, 7], [2, 4, 7]) == [1, 3] -@test s.shiftremove([1, 4, 5, 8], [2, 4, 7]) == [1, 3, 5] - -@test s.intersect([2, 3, 6], [1, 3, 4, 7]) == [3] -@test s.intersect([1, 3, 4, 7], [2, 3, 6]) == [3] -@test s.intersect([2, 3, 6], [1, 3]) == [3] -@test s.intersect([1, 3], [2, 3, 6]) == [3] -@test s.intersect([2, 3, 6], [1, 3, 6]) == [3, 6] -@test s.intersect([1, 3, 6], [2, 3, 6]) == [3, 6] -@test s.intersect(Int[], [2, 3]) == Int[] -@test s.intersect([2, 3], Int[]) == Int[] - -@test s.reducedindices([3, 5], [2, 3, 5, 6]) == [2, 3] -x = [3, 5] -s.reducedindices!(x, [2, 3, 5, 6]) -@test x == [2, 3] - -@test_throws AssertionError s.check_indices(5, [1, 6]) -@test_throws AssertionError s.check_indices(5, [0, 2]) -@test s.check_indices(5, Int[]) == nothing -@test s.check_indices(5, [1, 3]) == nothing -@test s.check_indices(5, [3, 1]) == nothing - -@test_throws AssertionError s.check_sortedindices(5, [1, 6]) -@test_throws AssertionError s.check_sortedindices(5, [3, 1]) -@test_throws AssertionError s.check_sortedindices(5, [0, 2]) -@test s.check_sortedindices(5, Int[]) == nothing -@test s.check_sortedindices(5, [1, 3]) == nothing - -end # testset diff --git a/test/test_sparsematrix.jl b/test/test_sparsematrix.jl deleted file mode 100644 index 476c7366..00000000 --- a/test/test_sparsematrix.jl +++ /dev/null @@ -1,35 +0,0 @@ -using Base.Test -using QuantumOptics.sparsematrix - -# SparseMatrix = quantumoptics.sparsematrix.SparseMatrix -const SparseMatrix = SparseMatrixCSC{Complex128, Int} - - -@testset "sparsematrix" begin - -# Set up test matrices -A = rand(Complex128, 5, 5) -A_sp = sparse(A) - -B = eye(Complex128, 5) -B_sp = speye(Complex128, 5) - -C = rand(Complex128, 3, 3) -C[2,:] = 0 -C_sp = sparse(C) - -R_sp = A_sp + B_sp -R = A + B - - -# Test arithmetic -@test 0 ≈ norm(full(R_sp) - R) -@test 0 ≈ norm(full(Complex128(0.5,0)*A_sp) - 0.5*A) -@test 0 ≈ norm(full(A_sp/2) - A/2) -@test 0 ≈ norm(full(A_sp*B_sp) - A*B) - -# Test kronecker product -@test 0 ≈ norm(full(kron(A_sp, C_sp)) - kron(A, C)) -@test 0 ≈ norm(full(kron(A_sp, B_sp)) - kron(A, B)) - -end # testset diff --git a/test/test_spectralanalysis.jl b/test/test_spectralanalysis.jl index ebff6831..34022f0c 100644 --- a/test/test_spectralanalysis.jl +++ b/test/test_spectralanalysis.jl @@ -1,28 +1,29 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra, SparseArrays, Random -mutable struct SpectralanalysisTestOperator <: Operator end +mutable struct SpectralanalysisTestOperator{BL<:Basis,BR<:Basis} <: AbstractOperator{BL,BR} end @testset "spectralanalysis" begin -srand(0) +Random.seed!(0) -sprandop(b) = sparse(DenseOperator(b, rand(Complex128, length(b), length(b)))) +sprandop(b) = sparse(DenseOperator(b, rand(ComplexF64, length(b), length(b)))) # Test diagonalization -@test_throws ArgumentError eigenstates(SpectralanalysisTestOperator()) -@test_throws bases.IncompatibleBases eigenstates(DenseOperator(GenericBasis(3), GenericBasis(4))) -@test_throws ArgumentError eigenenergies(SpectralanalysisTestOperator()) -@test_throws bases.IncompatibleBases eigenenergies(DenseOperator(GenericBasis(3), GenericBasis(4))) +@test_throws ArgumentError eigenstates(SpectralanalysisTestOperator{Basis,Basis}()) +@test_throws QuantumOpticsBase.IncompatibleBases eigenstates(DenseOperator(GenericBasis(3), GenericBasis(4))) +@test_throws ArgumentError eigenenergies(SpectralanalysisTestOperator{Basis,Basis}()) +@test_throws QuantumOpticsBase.IncompatibleBases eigenenergies(DenseOperator(GenericBasis(3), GenericBasis(4))) # Test hermitian diagonalization b = GenericBasis(5) a = randoperator(b) H = (a+dagger(a))/2 -U = expm(1im*H) +U = exp(1im*H) d = [-3, -2.6, -0.1, 0.0, 0.6] -D = DenseOperator(b, diagm(d)) +D = DenseOperator(b, diagm(0 => d)) Dsp = sparse(D) @test eigenenergies(D) ≈ d @test eigenenergies(Dsp, 3, info=false) ≈ d[1:3] @@ -48,9 +49,9 @@ end b = GenericBasis(5) a = randoperator(b) H = (a+dagger(a))/2 -U = expm(1im*H) +U = exp(1im*H) d = [-3+0.2im, -2.6-0.1im, -0.1+0.5im, 0.0, 0.6+0.3im] -D = DenseOperator(b, diagm(d)) +D = DenseOperator(b, diagm(0 => d)) Dsp = sparse(D) @test eigenenergies(D; warning=false) ≈ d @test eigenenergies(Dsp, 3; warning=false, info=false) ≈ d[1:3] @@ -78,9 +79,9 @@ sx = sigmax(spinbasis) sy = sigmay(spinbasis) sz = sigmaz(spinbasis) twospinbasis = spinbasis ⊗ spinbasis -Sx = full(sum([embed(twospinbasis, i, sx) for i=1:2]))/2. -Sy = full(sum([embed(twospinbasis, i, sy) for i=1:2]))/2. -Sz = full(sum([embed(twospinbasis, i, sz) for i=1:2]))/2. +Sx = dense(sum([embed(twospinbasis, i, sx) for i=1:2]))/2. +Sy = dense(sum([embed(twospinbasis, i, sy) for i=1:2]))/2. +Sz = dense(sum([embed(twospinbasis, i, sz) for i=1:2]))/2. Ssq = Sx^2 + Sy^2 + Sz^2 d, v = simdiag([Sz, Ssq]) @test d[1] == [-1.0, 0, 0, 1.0] @@ -88,16 +89,16 @@ d, v = simdiag([Sz, Ssq]) @test_throws ErrorException simdiag([Sx, Sy]) threespinbasis = spinbasis ⊗ spinbasis ⊗ spinbasis -Sx3 = full(sum([embed(threespinbasis, i, sx) for i=1:3])/2.) -Sy3 = full(sum([embed(threespinbasis, i, sy) for i=1:3])/2.) -Sz3 = full(sum([embed(threespinbasis, i, sz) for i=1:3])/2.) +Sx3 = dense(sum([embed(threespinbasis, i, sx) for i=1:3])/2.) +Sy3 = dense(sum([embed(threespinbasis, i, sy) for i=1:3])/2.) +Sz3 = dense(sum([embed(threespinbasis, i, sz) for i=1:3])/2.) Ssq3 = Sx3^2 + Sy3^2 + Sz3^2 d3, v3 = simdiag([Ssq3, Sz3]) -dsq3_std = eigenenergies(full(Ssq3)) -@test diagm(dsq3_std) ≈ v3'*Ssq3.data*v3 +dsq3_std = eigenenergies(dense(Ssq3)) +@test diagm(0 => dsq3_std) ≈ v3'*Ssq3.data*v3 fockbasis = FockBasis(4) @test_throws ErrorException simdiag([Sy3, Sz3]) -@test_throws ErrorException simdiag([full(destroy(fockbasis)), full(create(fockbasis))]) +@test_throws ErrorException simdiag([dense(destroy(fockbasis)), dense(create(fockbasis))]) end # testset diff --git a/test/test_spin.jl b/test/test_spin.jl deleted file mode 100644 index 84d9a385..00000000 --- a/test/test_spin.jl +++ /dev/null @@ -1,115 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "spin" begin - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) - -# Test creation -@test_throws AssertionError SpinBasis(1//3) -@test_throws AssertionError SpinBasis(-1//2) -@test_throws AssertionError SpinBasis(0) - - -for spinnumber=[1//2, 1, 3//2, 4//2] - spinbasis = SpinBasis(spinnumber) - I = operators.identityoperator(spinbasis) - Zero = SparseOperator(spinbasis) - sx = sigmax(spinbasis) - sy = sigmay(spinbasis) - sz = sigmaz(spinbasis) - sp = sigmap(spinbasis) - sm = sigmam(spinbasis) - - - # Test traces - @test 0 == trace(sx) - @test 0 == trace(sy) - @test 0 == trace(sz) - - - # Test kommutation relations - kommutator(x, y) = x*y - y*x - - @test 1e-12 > D(kommutator(sx, sx), Zero) - @test 1e-12 > D(kommutator(sx, sy), 2im*sz) - @test 1e-12 > D(kommutator(sx, sz), -2im*sy) - @test 1e-12 > D(kommutator(sy, sx), -2im*sz) - @test 1e-12 > D(kommutator(sy, sy), Zero) - @test 1e-12 > D(kommutator(sy, sz), 2im*sx) - @test 1e-12 > D(kommutator(sz, sx), 2im*sy) - @test 1e-12 > D(kommutator(sz, sy), -2im*sx) - @test 1e-12 > D(kommutator(sz, sz), Zero) - - - # Test creation and anihilation operators - @test 0 == D(sp, 0.5*(sx + 1im*sy)) - @test 0 == D(sm, 0.5*(sx - 1im*sy)) - @test 0 == D(sx, (sp + sm)) - @test 0 == D(sy, -1im*(sp - sm)) - - - # Test commutation relations with creation and anihilation operators - @test 1e-12 > D(kommutator(sp, sm), sz) - @test 1e-12 > D(kommutator(sz, sp), 2*sp) - @test 1e-12 > D(kommutator(sz, sm), -2*sm) - - - # Test v x (v x u) relation: [sa, [sa, sb]] = 4*(1-delta_{ab})*sb - @test 1e-12 > D(kommutator(sx, kommutator(sx, sx)), Zero) - @test 1e-12 > D(kommutator(sx, kommutator(sx, sy)), 4*sy) - @test 1e-12 > D(kommutator(sx, kommutator(sx, sz)), 4*sz) - @test 1e-12 > D(kommutator(sy, kommutator(sy, sx)), 4*sx) - @test 1e-12 > D(kommutator(sy, kommutator(sy, sy)), Zero) - @test 1e-12 > D(kommutator(sy, kommutator(sy, sz)), 4*sz) - @test 1e-12 > D(kommutator(sz, kommutator(sz, sx)), 4*sx) - @test 1e-12 > D(kommutator(sz, kommutator(sz, sy)), 4*sy) - @test 1e-12 > D(kommutator(sz, kommutator(sz, sz)), Zero) - - - # Test spinup and spindown states - @test 1 ≈ norm(spinup(spinbasis)) - @test 1 ≈ norm(spindown(spinbasis)) - @test 0 ≈ norm(sp*spinup(spinbasis)) - @test 0 ≈ norm(sm*spindown(spinbasis)) -end - - -# Test special relations for spin 1/2 - -spinbasis = SpinBasis(1//2) -I = identityoperator(spinbasis) -Zero = SparseOperator(spinbasis) -sx = sigmax(spinbasis) -sy = sigmay(spinbasis) -sz = sigmaz(spinbasis) -sp = sigmap(spinbasis) -sm = sigmam(spinbasis) - - -# Test antikommutator -antikommutator(x, y) = x*y + y*x - -@test 0 ≈ D(antikommutator(sx, sx), 2*I) -@test 0 ≈ D(antikommutator(sx, sy), Zero) -@test 0 ≈ D(antikommutator(sx, sz), Zero) -@test 0 ≈ D(antikommutator(sy, sx), Zero) -@test 0 ≈ D(antikommutator(sy, sy), 2*I) -@test 0 ≈ D(antikommutator(sy, sz), Zero) -@test 0 ≈ D(antikommutator(sz, sx), Zero) -@test 0 ≈ D(antikommutator(sz, sy), Zero) -@test 0 ≈ D(antikommutator(sz, sz), 2*I) - - -# Test if involutory for spin 1/2 -@test 0 ≈ D(sx*sx, I) -@test 0 ≈ D(sy*sy, I) -@test 0 ≈ D(sz*sz, I) -@test 0 ≈ D(-1im*sx*sy*sz, I) - - -# Test consistency of spin up and down with sigmap and sigmam -@test 1e-11 > norm(sm*spinup(spinbasis) - spindown(spinbasis)) -@test 1e-11 > norm(sp*spindown(spinbasis) - spinup(spinbasis)) - -end # testset diff --git a/test/test_state_definitions.jl b/test/test_state_definitions.jl deleted file mode 100644 index 58a173d9..00000000 --- a/test/test_state_definitions.jl +++ /dev/null @@ -1,40 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "state_definitions" begin - -n=30 -b=FockBasis(n) -omega=40.3 -T=2.3756 -r=thermalstate(omega*number(b),T) -for k=1:n-1 - @test isapprox(r.data[k+1,k+1]/r.data[k,k],exp(-omega/T)) -end -S=entropy_vn(r) -z=sum(exp.(-[0:n;]*omega)) -s=expect(omega*number(b),r)/T+log(z) -isapprox(S,s) - -alpha=rand()+im*rand() -r=coherentthermalstate(b,omega*number(b),T,alpha) -@test isapprox(expect(number(b),r),abs(alpha)^2+1/(exp(omega/T)-1)) -@test isapprox(expect(destroy(b),r),alpha) -@test isapprox(entropy_vn(r),S) - -rp=phase_average(r) -@test isapprox(expect(number(b),rp),abs(alpha)^2+1/(exp(omega/T)-1)) -@test isapprox(expect(destroy(b),rp),0) -for k=1:n - @test isapprox(rp.data[k,k],r.data[k,k]) -end - -rpas=passive_state(r) -for k=1:n-1 - @test real(rpas.data[k+1,k+1]) D(bra_b1 + Bra(b1), bra_b1) -@test 1e-14 > D(ket_b1 + Ket(b1), ket_b1) -@test 1e-14 > D(bra_b1 + dagger(ket_b1), dagger(ket_b1) + bra_b1) - -# Subtraction -@test_throws bases.IncompatibleBases bra_b1 - bra_b2 -@test_throws bases.IncompatibleBases ket_b1 - ket_b2 -@test 1e-14 > D(bra_b1 - Bra(b1), bra_b1) -@test 1e-14 > D(ket_b1 - Ket(b1), ket_b1) -@test 1e-14 > D(bra_b1 - dagger(ket_b1), -dagger(ket_b1) + bra_b1) - -# Multiplication -@test 1e-14 > D(-3*ket_b1, 3*(-ket_b1)) -@test 1e-14 > D(0.3*(bra_b1 - dagger(ket_b1)), 0.3*bra_b1 - dagger(0.3*ket_b1)) -@test 1e-14 > D(0.3*(bra_b1 - dagger(ket_b1)), bra_b1*0.3 - dagger(ket_b1*0.3)) -@test 0 ≈ bra*ket -@test 1e-14 > D((bra_b1 ⊗ bra_b2)*(ket_b1 ⊗ ket_b2), (bra_b1*ket_b1)*(bra_b2*ket_b2)) - -# Tensor product -@test tensor(ket_b1) == ket_b1 -@test 1e-14 > D((ket_b1 ⊗ ket_b2) ⊗ ket_b3, ket_b1 ⊗ (ket_b2 ⊗ ket_b3)) -@test 1e-14 > D((bra_b1 ⊗ bra_b2) ⊗ bra_b3, bra_b1 ⊗ (bra_b2 ⊗ bra_b3)) - -ket_b1b2 = ket_b1 ⊗ ket_b2 -shape = (ket_b1b2.basis.shape...) -idx = sub2ind(shape, 2, 3) -@test ket_b1b2.data[idx] == ket_b1.data[2]*ket_b2.data[3] -ket_b1b2b3 = ket_b1 ⊗ ket_b2 ⊗ ket_b3 -@test ket_b1b2b3 == tensor(ket_b1, ket_b2, ket_b3) -shape = (ket_b1b2b3.basis.shape...) -idx = sub2ind(shape, 1, 4, 3) -@test ket_b1b2b3.data[idx] == ket_b1.data[1]*ket_b2.data[4]*ket_b3.data[3] - - -# Norm -basis = FockBasis(1) -bra = Bra(basis, [3im, -4]) -ket = Ket(basis, [-4im, 3]) -@test 5 ≈ norm(bra) -@test 5 ≈ norm(ket) - -bra_normalized = normalize(bra) -ket_normalized = normalize(ket) -@test 5 ≈ norm(bra) -@test 5 ≈ norm(ket) -@test 1 ≈ norm(bra_normalized) -@test 1 ≈ norm(ket_normalized) - -bra_copy = deepcopy(bra) -ket_copy = deepcopy(ket) -normalize!(bra_copy) -normalize!(ket_copy) -@test 5 ≈ norm(bra) -@test 5 ≈ norm(ket) -@test 1 ≈ norm(bra_copy) -@test 1 ≈ norm(ket_copy) - -# Test basis state -b1 = GenericBasis(2) -b2 = GenericBasis(3) -b = b1 ⊗ b2 -x1 = basisstate(b1, 2) -x2 = basisstate(b2, 1) - -@test norm(x1) == 1 -@test x1.data[2] == 1 -@test basisstate(b, [2, 1]) == x1 ⊗ x2 - -# Test permutating systems -b1 = GenericBasis(2) -b2 = GenericBasis(5) -b3 = GenericBasis(3) - -psi1 = randstate(b1) -psi2 = randstate(b2) -psi3 = randstate(b3) - -psi123 = psi1 ⊗ psi2 ⊗ psi3 -psi132 = psi1 ⊗ psi3 ⊗ psi2 -psi213 = psi2 ⊗ psi1 ⊗ psi3 -psi231 = psi2 ⊗ psi3 ⊗ psi1 -psi312 = psi3 ⊗ psi1 ⊗ psi2 -psi321 = psi3 ⊗ psi2 ⊗ psi1 - -@test 1e-14 > D(psi132, permutesystems(psi123, [1, 3, 2])) -@test 1e-14 > D(psi213, permutesystems(psi123, [2, 1, 3])) -@test 1e-14 > D(psi231, permutesystems(psi123, [2, 3, 1])) -@test 1e-14 > D(psi312, permutesystems(psi123, [3, 1, 2])) -@test 1e-14 > D(psi321, permutesystems(psi123, [3, 2, 1])) - -@test 1e-14 > D(dagger(psi132), permutesystems(dagger(psi123), [1, 3, 2])) -@test 1e-14 > D(dagger(psi213), permutesystems(dagger(psi123), [2, 1, 3])) -@test 1e-14 > D(dagger(psi231), permutesystems(dagger(psi123), [2, 3, 1])) -@test 1e-14 > D(dagger(psi312), permutesystems(dagger(psi123), [3, 1, 2])) -@test 1e-14 > D(dagger(psi321), permutesystems(dagger(psi123), [3, 2, 1])) - -end # testset diff --git a/test/test_steadystate.jl b/test/test_steadystate.jl index 5b0e2584..4797b70d 100644 --- a/test/test_steadystate.jl +++ b/test/test_steadystate.jl @@ -1,5 +1,6 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra @testset "steadystate" begin @@ -27,13 +28,13 @@ Ha = embed(basis, 1, 0.5*ωa*sz) Hc = embed(basis, 2, ωc*number(fockbasis)) Hint = sm ⊗ create(fockbasis) + sp ⊗ destroy(fockbasis) H = Ha + Hc + Hint -Hdense = full(H) +Hdense = dense(H) Ja = embed(basis, 1, sqrt(γ)*sm) Ja2 = embed(basis, 1, sqrt(0.5*γ)*sp) Jc = embed(basis, 2, sqrt(κ)*destroy(fockbasis)) J = [Ja, Jc] -Jdense = map(full, J) +Jdense = map(dense, J) Ψ₀ = spinup(spinbasis) ⊗ fockstate(fockbasis, 2) ρ₀ = dm(Ψ₀) @@ -64,11 +65,11 @@ tss, ρss = steadystate.master(Hdense, Jdense; tol=1e-4) ev, ops = steadystate.liouvillianspectrum(Hdense, Jdense) @test tracedistance(ρss, ops[1]) < 1e-12 -@test ev[sortperm(-real(ev))] == ev +@test ev[sortperm(abs.(ev))] == ev ev, ops = steadystate.liouvillianspectrum(H, sqrt(2).*J; rates=0.5.*ones(length(J)), nev = 1) -@test tracedistance(ρss, ops[1]) < 1e-12 -@test ev[sortperm(-real(ev))] == ev +@test tracedistance(ρss, ops[1]/tr(ops[1])) < 1e-12 +@test ev[sortperm(abs.(ev))] == ev # Compute steady-state photon number of a driven cavity (analytically: η^2/κ^2) Hp = η*(destroy(fockbasis) + create(fockbasis)) @@ -83,7 +84,7 @@ nss = expect(create(fockbasis)*destroy(fockbasis), ρss[end]) nss = expect(create(fockbasis)*destroy(fockbasis), ρss) @test n_an - real(nss) < 1e-3 -ρss = steadystate.eigenvector(full(Hp), map(full, Jp)) +ρss = steadystate.eigenvector(dense(Hp), map(dense, Jp)) nss = expect(create(fockbasis)*destroy(fockbasis), ρss) @test n_an - real(nss) < 1e-3 diff --git a/test/test_stochastic_definitions.jl b/test/test_stochastic_definitions.jl index 48990d34..9b8f9ca6 100644 --- a/test/test_stochastic_definitions.jl +++ b/test/test_stochastic_definitions.jl @@ -1,4 +1,4 @@ -using Base.Test +using Test using QuantumOptics @testset "stochastic_definitions" begin diff --git a/test/test_stochastic_master.jl b/test/test_stochastic_master.jl index b47338e1..f11c5334 100644 --- a/test/test_stochastic_master.jl +++ b/test/test_stochastic_master.jl @@ -1,5 +1,6 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra @testset "stochastic_master" begin diff --git a/test/test_stochastic_schroedinger.jl b/test/test_stochastic_schroedinger.jl index 85422da6..9390a59e 100644 --- a/test/test_stochastic_schroedinger.jl +++ b/test/test_stochastic_schroedinger.jl @@ -1,5 +1,7 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra +import StochasticDiffEq @testset "stochastic_schroedinger" begin diff --git a/test/test_stochastic_semiclassical.jl b/test/test_stochastic_semiclassical.jl index 7ea20b4f..a1a82a2d 100644 --- a/test/test_stochastic_semiclassical.jl +++ b/test/test_stochastic_semiclassical.jl @@ -1,5 +1,6 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra @testset "stochastic_semiclassical" begin @@ -23,7 +24,7 @@ Jdagger = dagger.(J) C .*= rates Cdagger = dagger.(C) -u0 = Complex128[0.1, 0.5] +u0 = ComplexF64[0.1, 0.5] ψ_sc = semiclassical.State(ψ0, u0) ρ_sc = dm(ψ_sc) @@ -77,10 +78,10 @@ tout, ψt_sc = stochastic.schroedinger_semiclassical(T_short, ψ_sc, fquantum, f tout, ψt_sc = stochastic.schroedinger_semiclassical(T_short, ψ_sc, fquantum, fclassical; fstoch_quantum=fquantum_stoch, fstoch_classical=fclassical_stoch2, noise_processes=1, - noise_prototype_classical=zeros(Complex128, 2,2), dt=dt) + noise_prototype_classical=zeros(ComplexF64, 2,2), dt=dt) tout, ψt_sc = stochastic.schroedinger_semiclassical(T_short, ψ_sc, fquantum, fclassical; fstoch_classical=fclassical_stoch_ndiag, - noise_prototype_classical=zeros(Complex128, 2, 3), dt=dt) + noise_prototype_classical=zeros(ComplexF64, 2, 3), dt=dt) # Semiclassical master tout, ρt = stochastic.master_semiclassical(T, ρ_sc, fquantum_master, fclassical; @@ -89,7 +90,7 @@ tout, ρt = stochastic.master_semiclassical(T, ρ_sc, fquantum_master, fclassica fstoch_classical=fclassical_stoch, dt=dt) tout, ρt = stochastic.master_semiclassical(T, ψ_sc, fquantum_master, fclassical; fstoch_quantum=fstoch_q_master, fstoch_classical=fclassical_stoch2, - noise_prototype_classical=zeros(Complex128, 2, 2), dt=dt) + noise_prototype_classical=zeros(ComplexF64, 2, 2), dt=dt) # Test error messages @test_throws ArgumentError stochastic.schroedinger_semiclassical(T, ψ_sc, fquantum, fclassical) diff --git a/test/test_subspace.jl b/test/test_subspace.jl deleted file mode 100644 index 6d8370ce..00000000 --- a/test/test_subspace.jl +++ /dev/null @@ -1,43 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "subspace" begin - -b = FockBasis(3) - -u = Ket[fockstate(b, 1), fockstate(b, 2)] -v = Ket[fockstate(b, 2), fockstate(b, 1)] - -bu = SubspaceBasis(u) -bv = SubspaceBasis(v) - -T1 = projector(bu, b) -T2 = projector(bv, b) -T12 = projector(bu, bv) - -state = fockstate(b, 2) -state_u = Ket(bu, [0, 1]) -state_v = Ket(bv, [1., 0]) - -@test T1*state == state_u -@test T2*state == state_v - - -state_v = Ket(bv, [1, -1]) -state_u = Ket(bu, [-1, 1]) - -@test T12*state_v == state_u - -u2 = Ket[1.5*fockstate(b, 1), fockstate(b, 1) + fockstate(b, 2)] -bu2_orth = subspace.orthonormalize(SubspaceBasis(u)) -@test bu2_orth.basisstates == bu.basisstates - -# Test errors -b2 = FockBasis(4) -@test_throws ArgumentError SubspaceBasis(b2, u) -@test_throws ArgumentError projector(bu, b2) -@test_throws ArgumentError projector(b2, bu) -b2_sub = SubspaceBasis(b2, [fockstate(b2, 1)]) -@test_throws ArgumentError projector(bu, b2_sub) - -end # testset diff --git a/test/test_superoperators.jl b/test/test_superoperators.jl deleted file mode 100644 index 170bde3d..00000000 --- a/test/test_superoperators.jl +++ /dev/null @@ -1,185 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "superoperators" begin - -# Test creation -b = GenericBasis(3) -@test_throws DimensionMismatch DenseSuperOperator((b, b), (b, b), zeros(Complex128, 3, 3)) -@test_throws DimensionMismatch SparseSuperOperator((b, b), (b, b), spzeros(Complex128, 3, 3)) - -# Test copy, sparse and full -b1 = GenericBasis(2) -b2 = GenericBasis(7) -b3 = GenericBasis(5) -b4 = GenericBasis(3) - -s = DenseSuperOperator((b1, b2), (b3, b4)) -s_ = full(s) -s_.data[1,1] = 1 -@test s.data[1,1] == 0 -s_sparse = sparse(s_) -@test isa(s_sparse, SparseSuperOperator) -@test s_sparse.data[1,1] == 1 - -s = SparseSuperOperator((b1, b2), (b3, b4)) -s_ = sparse(s) -s_.data[1,1] = 1 -@test s.data[1,1] == 0 -s_full = full(s_) -@test isa(s_full, DenseSuperOperator) -@test s_full.data[1,1] == 1 - -# Test length -b1 = GenericBasis(3) -b2 = GenericBasis(5) -op = DenseOperator(b1, b2) -S = spre(op) -@test length(S) == length(S.data) == (3*5)^2 - -# Test arithmetic -b1 = GenericBasis(3) -b2 = GenericBasis(5) -b3 = GenericBasis(5) -b4 = GenericBasis(3) - -d1 = DenseSuperOperator((b1, b2), (b3, b4)) -d2 = DenseSuperOperator((b3, b4), (b1, b2)) -s1 = SparseSuperOperator((b1, b2), (b3, b4)) -s2 = SparseSuperOperator((b3, b4), (b1, b2)) - -x = d1*d2 -@test isa(x, DenseSuperOperator) -@test x.basis_l == x.basis_r == (b1, b2) - -x = s1*s2 -@test isa(x, SparseSuperOperator) -@test x.basis_l == x.basis_r == (b1, b2) - -x = d1*s2 -@test isa(x, DenseSuperOperator) -@test x.basis_l == x.basis_r == (b1, b2) - -x = s1*d2 -@test isa(x, DenseSuperOperator) -@test x.basis_l == x.basis_r == (b1, b2) - -x = d1*3 -@test isa(x, DenseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = 2.5*s1 -@test isa(x, SparseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = d1 + d1 -@test isa(x, DenseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = s1 + s1 -@test isa(x, SparseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = d1 + s1 -@test isa(x, DenseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = d1 - d1 -@test isa(x, DenseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = s1 - s1 -@test isa(x, SparseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = d1 - s1 -@test isa(x, DenseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = -d1 -@test isa(x, DenseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - -x = -s1 -@test isa(x, SparseSuperOperator) -@test x.basis_l == (b1, b2) -@test x.basis_r == (b3, b4) - - -# TODO: Clean-up this part -ωc = 1.2 -ωa = 0.9 -g = 1.0 -γ = 0.5 -κ = 1.1 - -T = Float64[0.,1.] - - -fockbasis = FockBasis(7) -spinbasis = SpinBasis(1//2) -basis = tensor(spinbasis, fockbasis) - -sx = sigmax(spinbasis) -sy = sigmay(spinbasis) -sz = sigmaz(spinbasis) -sp = sigmap(spinbasis) -sm = sigmam(spinbasis) - -Ha = embed(basis, 1, 0.5*ωa*sz) -Hc = embed(basis, 2, ωc*number(fockbasis)) -Hint = sm ⊗ create(fockbasis) + sp ⊗ destroy(fockbasis) -H = Ha + Hc + Hint - -Ja = embed(basis, 1, sqrt(γ)*sm) -Jc = embed(basis, 2, sqrt(κ)*destroy(fockbasis)) -J = [Ja, Jc] - -Ψ₀ = spinup(spinbasis) ⊗ fockstate(fockbasis, 5) -ρ₀ = dm(Ψ₀) - - -op1 = DenseOperator(spinbasis, [1.2+0.3im 0.7+1.2im;0.3+0.1im 0.8+3.2im]) -op2 = DenseOperator(spinbasis, [0.2+0.1im 0.1+2.3im; 0.8+4.0im 0.3+1.4im]) -@test tracedistance(spre(op1)*op2, op1*op2) < 1e-12 -@test tracedistance(spost(op1)*op2, op2*op1) < 1e-12 - -@test spre(sparse(op1))*op2 == op1*op2 -@test spost(sparse(op1))*op2 == op2*op1 - -@test spre(sparse(op1))*sparse(op2) == sparse(op1*op2) -@test spost(sparse(op1))*sparse(op2) == sparse(op2*op1) - -L = liouvillian(H, J) -ρ = -1im*(H*ρ₀ - ρ₀*H) -for j=J - ρ += j*ρ₀*dagger(j) - 0.5*dagger(j)*j*ρ₀ - 0.5*ρ₀*dagger(j)*j -end -@test tracedistance(L*ρ₀, ρ) < 1e-10 - -tout, ρt = timeevolution.master([0.,1.], ρ₀, H, J; reltol=1e-7) -@test tracedistance(expm(full(L))*ρ₀, ρt[end]) < 1e-6 - -@test full(spre(op1)) == spre(op1) - -@test_throws bases.IncompatibleBases L*op1 -@test_throws bases.IncompatibleBases L*spre(sm) - -@test L/2.0 == 0.5*L == L*0.5 -@test -L == SparseSuperOperator(L.basis_l, L.basis_r, -L.data) - -@test_throws AssertionError liouvillian(H, J; rates=zeros(4, 4)) - -rates = diagm([1.0, 1.0]) -@test liouvillian(H, J; rates=rates) == L - -end # testset diff --git a/test/test_timecorrelations.jl b/test/test_timecorrelations.jl index 83ef343c..0075d971 100644 --- a/test/test_timecorrelations.jl +++ b/test/test_timecorrelations.jl @@ -1,5 +1,6 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra @testset "timecorrelations" begin @@ -51,7 +52,7 @@ omega_sample = mod(n, 2) == 0 ? [-n/2:n/2-1;] : [-(n-1)/2:(n-1)/2;] omega_sample .*= 2pi/tspan[end] omega, S = timecorrelations.spectrum(omega_sample, H, J, op; rho_ss=ρ₀) -omega2, S2 = timecorrelations.spectrum(H, J, op) +omega2, S2 = timecorrelations.spectrum(H, J, op; tol=1e-3) @test length(omega2) == length(S2) omegaFFT, SFFT = timecorrelations.correlation2spectrum(tspan, exp_values) diff --git a/test/test_timeevolution_bloch_redfield.jl b/test/test_timeevolution_bloch_redfield.jl new file mode 100644 index 00000000..5937d437 --- /dev/null +++ b/test/test_timeevolution_bloch_redfield.jl @@ -0,0 +1,47 @@ +using QuantumOptics +using Test + + +@testset "bloch-redfield" begin + +Δ = 0.2 * 2*π +ϵ0 = 1.0 * 2*π +γ1 = 0.5 + +b = SpinBasis(1//2) +sx = sigmax(b) +sz = sigmaz(b) + +H = -Δ/2.0 * sx - ϵ0/2.0 * sz + +function ohmic_spectrum(ω) + ω == 0.0 ? (return γ1) : (return γ1/2 * (ω / (2*π)) * (ω > 0.0)) +end + +R, ekets = timeevolution.bloch_redfield_tensor(H, [[sx, ohmic_spectrum]]) + +known_result = [0.0+0.0im 0.0+0.0im 0.0+0.0im 0.245145+0.0im + 0.0+0.0im -0.161034-6.40762im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im -0.161034+6.40762im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 0.0+0.0im -0.245145+0.0im] +@test isapprox(dense(R).data, known_result, atol=1e-5) + +psi0 = spindown(b) +tout, ρt = timeevolution.master_bloch_redfield([0.0:0.1:2.0;], psi0, R, H) +rho_end = [0.38206-0.0im 0.0466443+0.0175017im + 0.0466443-0.0175017im 0.61794+0.0im] +@test isapprox(ρt[end].data, rho_end, atol=1e-5) +@test ρt[end] != ρt[end-1] +@test isa(ρt, Vector{<:DenseOperator}) + +# Test fout +fout(t,rho) = copy(rho) +tout, ρt2 = timeevolution.master_bloch_redfield([0.0:0.1:2.0;], psi0, R, H; fout=fout) +@test all(ρt .== ρt2) + +fout2(t,rho) = expect(sz,rho) +tout, z = timeevolution.master_bloch_redfield([0.0:0.1:2.0;], psi0, R, H; fout=fout2) +@test length(z) == length(ρt) +@test isa(z, Vector{ComplexF64}) + +end # testset diff --git a/test/test_timeevolution_master.jl b/test/test_timeevolution_master.jl index 96cae7cd..20768c61 100644 --- a/test/test_timeevolution_master.jl +++ b/test/test_timeevolution_master.jl @@ -1,5 +1,6 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra @testset "master" begin @@ -12,7 +13,7 @@ g = 1.0 T = Float64[0.,1.] -fockbasis = FockBasis(5) +fockbasis = FockBasis(10) spinbasis = SpinBasis(1//2) basis = tensor(spinbasis, fockbasis) @@ -38,19 +39,27 @@ Jlazy = [LazyTensor(basis, 1, sqrt(γ)*sm), Jc] Hnh = H - 0.5im*sum([dagger(J[i])*J[i] for i=1:length(J)]) -Hdense = full(H) +Hdense = dense(H) Hlazy = LazySum(Ha, Hc, Hint) -Hnh_dense = full(Hnh) -Junscaled_dense = map(full, Junscaled) -Jdense = map(full, J) +Hnh_dense = dense(Hnh) +Junscaled_dense = map(dense, Junscaled) +Jdense = map(dense, J) Ψ₀ = spinup(spinbasis) ⊗ fockstate(fockbasis, 5) ρ₀ = dm(Ψ₀) +# Test Liouvillian +L = liouvillian(H, J) +ρ = -1im*(H*ρ₀ - ρ₀*H) +for j=J + ρ .+= j*ρ₀*dagger(j) - 0.5*dagger(j)*j*ρ₀ - 0.5*ρ₀*dagger(j)*j +end +@test tracedistance(L*ρ₀, ρ) < 1e-10 # Test master tout, ρt = timeevolution.master(T, ρ₀, Hdense, Jdense; reltol=1e-7) ρ = ρt[end] +@test tracedistance(exp(dense(L)*T[end])*ρ₀, ρt[end]) < 1e-6 tout, ρt = timeevolution.master(T, ρ₀, H, J; reltol=1e-6) @test tracedistance(ρt[end], ρ) < 1e-5 @@ -136,7 +145,7 @@ R = [cos(alpha) -sin(alpha); sin(alpha) cos(alpha)] Rt = transpose(R) Jrotated_dense = [R[1,1]*Junscaled_dense[1] + R[1,2]*Junscaled_dense[2], R[2,1]*Junscaled_dense[1] + R[2,2]*Junscaled_dense[2]] Jrotated = [SparseOperator(j) for j=Jrotated_dense] -rates_matrix = diagm(rates_vector) +rates_matrix = diagm(0 => rates_vector) rates_matrix_rotated = R * rates_matrix * Rt tout, ρt = timeevolution.master(T, ρ₀, Hdense, Jrotated_dense; rates=rates_matrix_rotated, reltol=1e-7) diff --git a/test/test_timeevolution_mcwf.jl b/test/test_timeevolution_mcwf.jl index 8e58d4b6..6cec3c30 100644 --- a/test/test_timeevolution_mcwf.jl +++ b/test/test_timeevolution_mcwf.jl @@ -1,5 +1,6 @@ -using Base.Test +using Test using QuantumOptics +using Random, LinearAlgebra @testset "mcwf" begin @@ -29,14 +30,14 @@ Ha = embed(basis, 1, 0.5*ωa*sz) Hc = embed(basis, 2, ωc*number(fockbasis)) Hint = sm ⊗ create(fockbasis) + sp ⊗ destroy(fockbasis) H = Ha + Hc + Hint -Hdense = full(H) +Hdense = dense(H) Hlazy = LazySum(Ha, Hc, Hint) # Jump operators Ja = embed(basis, 1, sqrt(γ)*sm) Jc = embed(basis, 2, sqrt(κ)*destroy(fockbasis)) J = [Ja, Jc] -Jdense = map(full, J) +Jdense = map(dense, J) Jlazy = [LazyTensor(basis, 1, sqrt(γ)*sm), LazyTensor(basis, 2, sqrt(κ)*destroy(fockbasis))] # Initial conditions @@ -93,7 +94,7 @@ tout, Ψt = timeevolution.mcwf_h(T, Ψ₀, H, J; seed=UInt(2), reltol=1e-6) # Test mcwf nh Hnh = H - 0.5im*sum([dagger(J[i])*J[i] for i=1:length(J)]) -Hnh_dense = full(Hnh) +Hnh_dense = dense(Hnh) tout, Ψt = timeevolution.mcwf_nh(T, Ψ₀, Hnh, J; seed=UInt(1), reltol=1e-6) @test norm(Ψt[end]-Ψ) < 1e-5 @@ -127,9 +128,9 @@ end # Test single jump operator J1 = [Ja] -J1_dense = map(full, J1) +J1_dense = map(dense, J1) J2 = [Ja, 0 * Jc] -J2_dense = map(full, J2) +J2_dense = map(dense, J2) tout_master, ρt_master = timeevolution.master(T, ρ₀, Hdense, J1_dense) @@ -152,6 +153,13 @@ for i=1:length(T) @test tracedistance(ρt_master[i], ρ_average_3[i]) < 0.1 end +# Test displaying before/after jump +tout, Ψt = timeevolution.mcwf([T[1],T[end]], Ψ₀, Hdense, J1; seed=2, display_beforeevent=true, display_afterevent=true) +for i=2:length(tout)-1 + if tout[i+1] == tout[i] + @test Ψt[i+1].data ≈ normalize(J1[1]*Ψt[i]).data + end +end # Test equivalence to schroedinger time evolution for no decay J = DenseOperator[] @@ -197,7 +205,7 @@ end J3_lazy = [LazyTensor(threespinbasis, i, sm) for i=1:3] d, diagJ_lazy = diagonaljumps(rates, J3_lazy) for i=1:3 - @test full(diagJ_lazy[i]) == full(diagJ[i]) + @test dense(diagJ_lazy[i]) == dense(diagJ[i]) end # Test dynamic @@ -248,5 +256,18 @@ for i=1:length(T) @test tracedistance(ρt_master[i], ρ_average_4[i]) < 0.1 end +# Test displaying of jumps +tout, Ψt, t_jump, j_index = timeevolution.mcwf(T, Ψ₀, Hdense, Jdense; seed=UInt(1), reltol=1e-7, display_jumps=true) +tout, Ψt, t_jump2, j_index2 = timeevolution.mcwf(T, Ψ₀, Hdense, Jdense; seed=UInt(1), reltol=1e-7, display_jumps=true) + +@test length(j_index) == length(t_jump) == length(t_jump2) == length(j_index2) +@test j_index == j_index2 +@test t_jump == t_jump2 + +ψ0 = spinup(spinbasis)⊗fockstate(fockbasis,0) +tout, ψt, t_jump, j_index = timeevolution.mcwf(T, ψ0, 0Hdense, Jdense; display_jumps=true, seed=2) + +@test length(t_jump) == length(j_index) == 1 +@test j_index[1] == 1 end # testset diff --git a/test/test_timeevolution_pumpedcavity.jl b/test/test_timeevolution_pumpedcavity.jl index ecaf140e..83c1c120 100644 --- a/test/test_timeevolution_pumpedcavity.jl +++ b/test/test_timeevolution_pumpedcavity.jl @@ -1,4 +1,4 @@ -using Base.Test +using Test using QuantumOptics @testset "timeevolution_pumpedcavity" begin @@ -57,7 +57,7 @@ timeevolution.master_dynamic(T, psi0, f_HJ; fout=f_test_td) # Decay Hint_nh = Hint - 0.5im*κ*n -Γ = Matrix{Float64}(1,1) +Γ = Matrix{Float64}(undef, 1,1) Γ[1,1] = κ J = [a] Jdagger = [at] @@ -78,7 +78,7 @@ f_HJ_nh_dynamic2(t, rho) = (Hnh=f_H(t, psi0) - 0.5im*κ*n; (Hnh, dagger(Hnh), [s f_HJ_nh_dynamic3(t, rho) = (Hnh=f_H(t, psi0) - 0.5im*κ*n; (Hnh, dagger(Hnh), J, Jdagger, Γ)) timeevolution.master_dynamic(T, psi0, f_HJ_dynamic; rates=Γ, fout=f_test_decay_dynamic) -timeevolution.master_dynamic(T, psi0, f_HJ_dynamic2; fout=f_test_decay_dynamic) +@skiptimechecks timeevolution.master_dynamic(T, psi0, f_HJ_dynamic2; fout=f_test_decay_dynamic) timeevolution.master_nh_dynamic(T, psi0, f_HJ_nh_dynamic; rates=Γ, fout=f_test_decay_dynamic) timeevolution.master_nh_dynamic(T, psi0, f_HJ_nh_dynamic2; fout=f_test_decay_dynamic) timeevolution.master_nh_dynamic(T, psi0, f_HJ_nh_dynamic3; fout=f_test_decay_dynamic) diff --git a/test/test_timeevolution_schroedinger.jl b/test/test_timeevolution_schroedinger.jl index 893bddc6..fe5d2474 100644 --- a/test/test_timeevolution_schroedinger.jl +++ b/test/test_timeevolution_schroedinger.jl @@ -1,4 +1,4 @@ -using Base.Test +using Test using QuantumOptics @testset "schroedinger" begin @@ -51,14 +51,15 @@ end tout, psi_rot_t = timeevolution.schroedinger(T, psi0, Hrot) tout, psi_t = timeevolution.schroedinger_dynamic(T, psi0, f) +n_op = dense(at*a) for (i, t) in enumerate(tout) - R = prod([embed(basis, i, expm(1im*ω[i]*t*full(at*a))) for i=1:N]) + R = prod([embed(basis, i, exp(1im*ω[i]*t*n_op)) for i=1:N]) psi_rot = psi_rot_t[i] psi = psi_t[i] # @test abs(dagger(psi_rot)*R*psi) < 1e-5 rho = dm(psi) rho_rot = dm(psi_rot) - @test tracedistance(rho_rot, full(R)*rho*dagger(full(R))) < 1e-5 + @test tracedistance(rho_rot, dense(R)*rho*dagger(dense(R))) < 1e-5 end function fout(t, psi) diff --git a/test/test_timeevolution_twolevel.jl b/test/test_timeevolution_twolevel.jl index b59e3477..23a4d377 100644 --- a/test/test_timeevolution_twolevel.jl +++ b/test/test_timeevolution_twolevel.jl @@ -1,5 +1,6 @@ -using Base.Test +using Test using QuantumOptics +using LinearAlgebra @testset "timeevolution_twolevel" begin diff --git a/test/test_transformations.jl b/test/test_transformations.jl deleted file mode 100644 index f32d2766..00000000 --- a/test/test_transformations.jl +++ /dev/null @@ -1,42 +0,0 @@ -using Base.Test -using QuantumOptics - -@testset "fock" begin - -srand(0) - -D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2))) -D(x::Ket, y::Ket) = norm(x-y) - -# Test transformations -b_fock = FockBasis(20) -b_position = PositionBasis(-10, 10, 200) -Txn = transform(b_position, b_fock) -Tnx = transform(b_fock, b_position) -@test 1e-10 > D(dagger(Txn), Tnx) -@test 1e-10 > D(one(b_fock), Tnx*Txn) - -x0 = 0.1 -p0 = 0.3 -α0 = (x0 + 1im*p0)/sqrt(2) -psi_n = coherentstate(b_fock, α0) -psi_x = gaussianstate(b_position, x0, p0, 1) -@test 1e-10 > D(psi_x, Txn*psi_n) - -# Test different characteristic length -x0 = 0.0 -p0 = 0.2 -α0 = (x0 + 1im*p0)/sqrt(2) -σ0 = 0.7 -Txn = transform(b_position, b_fock; x0=σ0) -Tnx = transform(b_fock, b_position; x0=σ0) -@test 1e-10 > D(dagger(Txn), Tnx) -@test 1e-10 > D(one(b_fock), Tnx*Txn) - -psi_n = coherentstate(b_fock, α0) -psi_x = gaussianstate(b_position, x0/σ0, p0/σ0, σ0) -@test 1e-10 > D(psi_x, Txn*psi_n) - - - -end # testset \ No newline at end of file