From fde3be5c795c9dff8e2585a3b806c0bac1965d2b Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Sun, 8 Dec 2024 22:16:21 +0900 Subject: [PATCH 1/7] improve `QobjEvo` construction --- src/qobj/quantum_object_evo.jl | 19 ++++++++++++++++--- test/core-test/quantum_objects_evo.jl | 3 +++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/qobj/quantum_object_evo.jl b/src/qobj/quantum_object_evo.jl index cec0010e5..9e3fba557 100644 --- a/src/qobj/quantum_object_evo.jl +++ b/src/qobj/quantum_object_evo.jl @@ -180,8 +180,12 @@ function QuantumObjectEvolution( return QuantumObjectEvolution(data, type, dims) end -QuantumObjectEvolution(op::QuantumObject, f::Function; type::Union{Nothing,QuantumObjectType} = nothing) = - QuantumObjectEvolution(((op, f),); type = type) +# this is a extra method if user accidentally specify `QuantumObjectEvolution( (op, func) )` or `QuantumObjectEvolution( ((op, func)) )` +QuantumObjectEvolution(op_func::Tuple{QuantumObject,Function}, α::Union{Nothing,Number} = nothing; type::Union{Nothing,QuantumObjectType} = nothing) = + QuantumObjectEvolution((op_func,), α; type = type) + +QuantumObjectEvolution(op::QuantumObject, f::Function, α::Union{Nothing,Number} = nothing; type::Union{Nothing,QuantumObjectType} = nothing) = + QuantumObjectEvolution(((op, f),), α; type = type) function QuantumObjectEvolution( op::QuantumObject, @@ -229,6 +233,7 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution` N = length(op_func_list_types) dims_expr = () + func_methods_expr = () first_op = nothing data_expr = :(0) qobj_expr_const = :(0) @@ -236,6 +241,7 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution` for i in 1:N op_func_type = op_func_list_types[i] if op_func_type <: Tuple + # check the structure of the tuple length(op_func_type.parameters) == 2 || throw(ArgumentError("The tuple must have two elements.")) op_type = op_func_type.parameters[1] func_type = op_func_type.parameters[2] @@ -248,6 +254,7 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution` op = :(op_func_list[$i][1]) data_type = op_type.parameters[1] dims_expr = (dims_expr..., :($op.dims)) + func_methods_expr = (func_methods_expr..., :(methods(op_func_list[$i][2], [Any, Real]))) # [Any, Real] means each func must accept 2 arguments if i == 1 first_op = :($op) end @@ -267,10 +274,16 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution` end quote + # check the dims of the operators dims = tuple($(dims_expr...)) - allequal(dims) || throw(ArgumentError("The dimensions of the operators must be the same.")) + # check if each func accepts 2 arguments + func_methods = tuple($(func_methods_expr...)) + for f_method in func_methods + length(f_method.ms) == 0 && throw(ArgumentError("The following function must accept two arguments: `$(f_method.mt.name)(p, t)` with t<:Real")) + end + data_expr_const = $qobj_expr_const isa Integer ? $qobj_expr_const : _make_SciMLOperator($qobj_expr_const, α) data_expr = data_expr_const + $data_expr diff --git a/test/core-test/quantum_objects_evo.jl b/test/core-test/quantum_objects_evo.jl index d714abb6e..dfe837ed3 100644 --- a/test/core-test/quantum_objects_evo.jl +++ b/test/core-test/quantum_objects_evo.jl @@ -182,6 +182,7 @@ @test isconstant(H_td) == false @test isconstant(QobjEvo(a)) == true @test isoper(H_td) == true + @test QobjEvo(a, coef1) == QobjEvo((a, coef1)) # SuperOperator X = a * a' @@ -205,7 +206,9 @@ @test isconstant(L_td) == false @test issuper(L_td) == true + coef_wrong(t) = exp(-t) @test_logs (:warn,) (:warn,) liouvillian(H_td * H_td) # warnings from lazy tensor + @test_throws ArgumentError QobjEvo(a, coef_wrong) @test_throws MethodError QobjEvo([[a, coef1], a' * a, [a', coef2]]) @test_throws ArgumentError H_td(ρvec, p, t) @test_throws ArgumentError cache_operator(H_td, ρvec) From d590f3546e9766b0c2de901f683417e67f8b1fd4 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Sun, 8 Dec 2024 22:21:11 +0900 Subject: [PATCH 2/7] update changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b61c2caa..e44f80ddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/qutip/QuantumToolbox.jl/tree/main) - +- Improve the construction of `QobjEvo` ([#338], [#339]): + - add new methods to generate `QobjEvo` with input type `Tuple{QuantumObject,Function}`. + - add argument checks for each coefficient `Function`. ## [v0.23.1] Release date: 2024-12-06 @@ -53,3 +55,5 @@ Release date: 2024-11-13 [#324]: https://github.com/qutip/QuantumToolbox.jl/issues/324 [#330]: https://github.com/qutip/QuantumToolbox.jl/issues/330 [#335]: https://github.com/qutip/QuantumToolbox.jl/issues/335 +[#338]: https://github.com/qutip/QuantumToolbox.jl/issues/338 +[#339]: https://github.com/qutip/QuantumToolbox.jl/issues/339 From c5c69adbe1cf7aa1cf5030a3b57bd421e38fe006 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Sun, 8 Dec 2024 22:23:06 +0900 Subject: [PATCH 3/7] minor change --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e44f80ddc..94fb28eea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/qutip/QuantumToolbox.jl/tree/main) -- Improve the construction of `QobjEvo` ([#338], [#339]): - - add new methods to generate `QobjEvo` with input type `Tuple{QuantumObject,Function}`. - - add argument checks for each coefficient `Function`. +- Improve the construction of `QobjEvo`. ([#338], [#339]) ## [v0.23.1] Release date: 2024-12-06 From b26f6e3dc96563f4db1c2bde12ea9d0d7f813c89 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Sun, 8 Dec 2024 22:26:44 +0900 Subject: [PATCH 4/7] format files --- src/qobj/quantum_object_evo.jl | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/qobj/quantum_object_evo.jl b/src/qobj/quantum_object_evo.jl index 9e3fba557..ddef77646 100644 --- a/src/qobj/quantum_object_evo.jl +++ b/src/qobj/quantum_object_evo.jl @@ -181,11 +181,18 @@ function QuantumObjectEvolution( end # this is a extra method if user accidentally specify `QuantumObjectEvolution( (op, func) )` or `QuantumObjectEvolution( ((op, func)) )` -QuantumObjectEvolution(op_func::Tuple{QuantumObject,Function}, α::Union{Nothing,Number} = nothing; type::Union{Nothing,QuantumObjectType} = nothing) = - QuantumObjectEvolution((op_func,), α; type = type) +QuantumObjectEvolution( + op_func::Tuple{QuantumObject,Function}, + α::Union{Nothing,Number} = nothing; + type::Union{Nothing,QuantumObjectType} = nothing, +) = QuantumObjectEvolution((op_func,), α; type = type) -QuantumObjectEvolution(op::QuantumObject, f::Function, α::Union{Nothing,Number} = nothing; type::Union{Nothing,QuantumObjectType} = nothing) = - QuantumObjectEvolution(((op, f),), α; type = type) +QuantumObjectEvolution( + op::QuantumObject, + f::Function, + α::Union{Nothing,Number} = nothing; + type::Union{Nothing,QuantumObjectType} = nothing, +) = QuantumObjectEvolution(((op, f),), α; type = type) function QuantumObjectEvolution( op::QuantumObject, @@ -281,7 +288,11 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution` # check if each func accepts 2 arguments func_methods = tuple($(func_methods_expr...)) for f_method in func_methods - length(f_method.ms) == 0 && throw(ArgumentError("The following function must accept two arguments: `$(f_method.mt.name)(p, t)` with t<:Real")) + length(f_method.ms) == 0 && throw( + ArgumentError( + "The following function must accept two arguments: `$(f_method.mt.name)(p, t)` with t<:Real", + ), + ) end data_expr_const = $qobj_expr_const isa Integer ? $qobj_expr_const : _make_SciMLOperator($qobj_expr_const, α) From 7957ef845265a4ecc0bc90f5839883e711f672db Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Mon, 9 Dec 2024 01:16:55 +0900 Subject: [PATCH 5/7] add type inferrence tests for `QobjEvo` --- test/core-test/quantum_objects_evo.jl | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/core-test/quantum_objects_evo.jl b/test/core-test/quantum_objects_evo.jl index dfe837ed3..a10416a84 100644 --- a/test/core-test/quantum_objects_evo.jl +++ b/test/core-test/quantum_objects_evo.jl @@ -119,9 +119,9 @@ "Quantum Object Evo.: type=SuperOperator dims=$L_dims size=$L_size ishermitian=$L_isherm isconstant=$L_isconst\n$datastring" end - @testset "Type Inference (QuantumObject)" begin + @testset "Type Inference (QobjEvo)" begin + N = 4 for T in [ComplexF32, ComplexF64] - N = 4 a = MatrixOperator(rand(T, N, N)) @inferred QobjEvo(a) for type in [Operator, SuperOperator] @@ -129,6 +129,14 @@ end end + a = destroy(N) + coef1(p, t) = exp(-t) + coef2(p::Vector, t) = sin(p[1] * t) + coef3(p::NamedTuple, t) = cos(p.ω * t) + @inferred QobjEvo(a, coef1) + @inferred QobjEvo((a', coef2)) + @inferred QobjEvo((a' * a, (a, coef1), (a', coef2), (a + a', coef3))) + @testset "Math Operation" begin a = QobjEvo(destroy(20)) σx = QobjEvo(sigmax()) @@ -206,9 +214,11 @@ @test isconstant(L_td) == false @test issuper(L_td) == true - coef_wrong(t) = exp(-t) + coef_wrong1(t) = nothing + coef_wrong2(p, t::ComplexF64) = nothing @test_logs (:warn,) (:warn,) liouvillian(H_td * H_td) # warnings from lazy tensor - @test_throws ArgumentError QobjEvo(a, coef_wrong) + @test_throws ArgumentError QobjEvo(a, coef_wrong1) + @test_throws ArgumentError QobjEvo(a, coef_wrong2) @test_throws MethodError QobjEvo([[a, coef1], a' * a, [a', coef2]]) @test_throws ArgumentError H_td(ρvec, p, t) @test_throws ArgumentError cache_operator(H_td, ρvec) From 49fb3dcb67ada3ebc832e1f4f359f19fa4f97b92 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Tue, 10 Dec 2024 11:06:13 +0900 Subject: [PATCH 6/7] imporve code quality --- src/time_evolution/sesolve.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time_evolution/sesolve.jl b/src/time_evolution/sesolve.jl index 0c5a3305f..414933de6 100644 --- a/src/time_evolution/sesolve.jl +++ b/src/time_evolution/sesolve.jl @@ -2,7 +2,7 @@ export sesolveProblem, sesolve _sesolve_make_U_QobjEvo(H::QuantumObjectEvolution{<:MatrixOperator}) = QobjEvo(MatrixOperator(-1im * H.data.A), dims = H.dims, type = Operator) -_sesolve_make_U_QobjEvo(H) = QobjEvo(H, -1im) +_sesolve_make_U_QobjEvo(H::Union{QuantumObjectEvolution,Tuple}) = QobjEvo(H, -1im) @doc raw""" sesolveProblem( From d5834affbd48b3f44eff467e7fa412d257c09844 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Tue, 10 Dec 2024 12:57:53 +0900 Subject: [PATCH 7/7] fix generating `U` in `sesolve` --- src/time_evolution/sesolve.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/time_evolution/sesolve.jl b/src/time_evolution/sesolve.jl index 414933de6..28562e280 100644 --- a/src/time_evolution/sesolve.jl +++ b/src/time_evolution/sesolve.jl @@ -2,6 +2,7 @@ export sesolveProblem, sesolve _sesolve_make_U_QobjEvo(H::QuantumObjectEvolution{<:MatrixOperator}) = QobjEvo(MatrixOperator(-1im * H.data.A), dims = H.dims, type = Operator) +_sesolve_make_U_QobjEvo(H::QuantumObject) = QobjEvo(MatrixOperator(-1im * H.data), dims = H.dims, type = Operator) _sesolve_make_U_QobjEvo(H::Union{QuantumObjectEvolution,Tuple}) = QobjEvo(H, -1im) @doc raw"""