diff --git a/Manifest.toml b/Manifest.toml index 9d7cc1f7..dc1def07 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -2,18 +2,21 @@ julia_version = "1.10.3" manifest_format = "2.0" -project_hash = "0af13b9126caf21a7349df842fd536f87be35b2f" +project_hash = "8f699eaced2984284612f4735ecfb535e1c71437" [[deps.ADTypes]] -git-tree-sha1 = "7a6b285f217ba92b5b474b783b4c2e8cf8218aaa" +git-tree-sha1 = "99a6f5d0ce1c7c6afdb759daa30226f71c54f6b0" uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" -version = "1.5.3" -weakdeps = ["ChainRulesCore", "EnzymeCore"] +version = "1.7.1" [deps.ADTypes.extensions] ADTypesChainRulesCoreExt = "ChainRulesCore" ADTypesEnzymeCoreExt = "EnzymeCore" + [deps.ADTypes.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + [[deps.ASL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "6252039f98492252f9e47c312c8ffda0e3b9e78d" @@ -61,9 +64,9 @@ version = "0.4.5" [[deps.Accessors]] deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown", "Test"] -git-tree-sha1 = "c0d491ef0b135fd7d63cbc6404286bc633329425" +git-tree-sha1 = "f61b15be1d76846c0ce31d3fcfac5380ae53db6a" uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" -version = "0.1.36" +version = "0.1.37" [deps.Accessors.extensions] AccessorsAxisKeysExt = "AxisKeys" @@ -90,6 +93,11 @@ weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" +[[deps.AdaptivePredicates]] +git-tree-sha1 = "7d5da5dd472490d048b081ca1bda4a7821b06456" +uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" +version = "1.1.1" + [[deps.AliasTables]] deps = ["PtrArrays", "Random"] git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" @@ -106,17 +114,11 @@ version = "0.4.1" uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" -[[deps.ArnoldiMethod]] -deps = ["LinearAlgebra", "Random", "StaticArrays"] -git-tree-sha1 = "d57bd3762d308bded22c3b82d033bff85f6195c6" -uuid = "ec485272-7323-5ecc-a04f-4719b315124d" -version = "0.4.0" - [[deps.ArrayInterface]] -deps = ["Adapt", "LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "5c9b74c973181571deb6442d41e5c902e6b9f38e" +deps = ["Adapt", "LinearAlgebra"] +git-tree-sha1 = "f54c23a5d304fb87110de62bace7777d59088c34" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.12.0" +version = "7.15.0" [deps.ArrayInterface.extensions] ArrayInterfaceBandedMatricesExt = "BandedMatrices" @@ -126,6 +128,7 @@ version = "7.12.0" ArrayInterfaceChainRulesExt = "ChainRules" ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" ArrayInterfaceReverseDiffExt = "ReverseDiff" + ArrayInterfaceSparseArraysExt = "SparseArrays" ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" ArrayInterfaceTrackerExt = "Tracker" @@ -137,19 +140,10 @@ version = "7.12.0" ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" -[[deps.ArrayLayouts]] -deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "8556500c18fcad8b4c44058e23fbc4a36143f6be" -uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.10.1" -weakdeps = ["SparseArrays"] - - [deps.ArrayLayouts.extensions] - ArrayLayoutsSparseArraysExt = "SparseArrays" - [[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" @@ -185,12 +179,6 @@ git-tree-sha1 = "95f5c7e2d177b7ba1a240b0518038b975d72a8c0" uuid = "e2ed5e7c-b2de-5872-ae92-c73ca462fb04" version = "0.1.7" -[[deps.BitTwiddlingConvenienceFunctions]] -deps = ["Static"] -git-tree-sha1 = "f21cfd4950cb9f0587d5067e69405ad2acd27b87" -uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b" -version = "0.1.6" - [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" @@ -202,12 +190,6 @@ git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc" uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" version = "0.5.0" -[[deps.CPUSummary]] -deps = ["CpuId", "IfElse", "PrecompileTools", "Static"] -git-tree-sha1 = "5a97e67919535d6841172016c9530fd69494e5ec" -uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" -version = "0.2.6" - [[deps.CRC32c]] uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" @@ -219,9 +201,9 @@ version = "1.0.1+0" [[deps.Cairo]] deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] -git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b" +git-tree-sha1 = "7b6ad8c35f4bc3bca8eb78127c8b99719506a5fb" uuid = "159f3aea-2a34-519c-b102-8c37f9878175" -version = "1.0.5" +version = "1.1.0" [[deps.CairoMakie]] deps = ["CRC32c", "Cairo", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] @@ -235,12 +217,6 @@ git-tree-sha1 = "a2f1c8c668c8e3cb4cca4e57a8efdb09067bb3fd" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" version = "1.18.0+2" -[[deps.Calculus]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" -uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" -version = "0.5.1" - [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] git-tree-sha1 = "71acdbf594aab5bbb2cec89b208c41b4c411e49f" @@ -251,23 +227,17 @@ weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] ChainRulesCoreSparseArraysExt = "SparseArrays" -[[deps.CloseOpenIntervals]] -deps = ["Static", "StaticArrayInterface"] -git-tree-sha1 = "05ba0d07cd4fd8b7a39541e31a7b0254704ea581" -uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9" -version = "0.1.13" - [[deps.CodecBzip2]] -deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"] -git-tree-sha1 = "f8889d1770addf59d0a015c49a473fa2bdb9f809" +deps = ["Bzip2_jll", "TranscodingStreams"] +git-tree-sha1 = "e7c529cc31bb85b97631b922fa2e6baf246f5905" uuid = "523fee87-0ab8-5b00-afb7-3ecf72e48cfd" -version = "0.8.3" +version = "0.8.4" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "b8fe8546d52ca154ac556809e10c75e6e7430ac8" +git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.5" +version = "0.7.6" [[deps.ColorBrewer]] deps = ["Colors", "JSON", "Test"] @@ -277,9 +247,9 @@ version = "0.4.0" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "4b270d6465eb21ae89b732182c20dc165f8bf9f2" +git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.25.0" +version = "3.26.0" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] @@ -326,9 +296,9 @@ version = "1.0.0" [[deps.Compat]] deps = ["TOML", "UUIDs"] -git-tree-sha1 = "b1c55339b7c6c350ee89f2c1604299660525b248" +git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.15.0" +version = "4.16.0" weakdeps = ["Dates", "LinearAlgebra"] [deps.Compat.extensions] @@ -353,11 +323,6 @@ weakdeps = ["InverseFunctions"] [deps.CompositionsBase.extensions] CompositionsBaseInverseFunctionsExt = "InverseFunctions" -[[deps.ConcreteStructs]] -git-tree-sha1 = "f749037478283d372048690eb3b5f92a79432b34" -uuid = "2569d6c7-a4a2-43d3-a901-331e8e4be471" -version = "0.2.3" - [[deps.Conda]] deps = ["Downloads", "JSON", "VersionParsing"] git-tree-sha1 = "b19db3927f0db4151cb86d073689f2428e524576" @@ -366,9 +331,9 @@ version = "1.10.2" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] -git-tree-sha1 = "260fd2400ed2dab602a7c15cf10c1933c59930a2" +git-tree-sha1 = "a33b7ced222c6165f624a3f2b55945fac5a598d9" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.5" +version = "1.5.7" weakdeps = ["IntervalSets", "StaticArrays"] [deps.ConstructionBase.extensions] @@ -380,12 +345,6 @@ git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" version = "0.6.3" -[[deps.CpuId]] -deps = ["Markdown"] -git-tree-sha1 = "fcbb72b032692610bfbdb15018ac16a36cf2e406" -uuid = "adafc99b-e345-5852-983c-f28acb93d879" -version = "0.3.1" - [[deps.DataAPI]] git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" @@ -407,52 +366,10 @@ deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" [[deps.DelaunayTriangulation]] -deps = ["EnumX", "ExactPredicates", "Random"] -git-tree-sha1 = "b0cb128d2e100646573e1da8565b02491fddb5ef" +deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"] +git-tree-sha1 = "b5f1c6532d2ea71e99b74231b0a3d53fba846ced" uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" -version = "1.0.4" - -[[deps.DiffEqBase]] -deps = ["ArrayInterface", "ConcreteStructs", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces"] -git-tree-sha1 = "d1e8a4642e28b0945bde6e2e1ac569b9e0abd728" -uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.151.5" - - [deps.DiffEqBase.extensions] - DiffEqBaseCUDAExt = "CUDA" - DiffEqBaseChainRulesCoreExt = "ChainRulesCore" - DiffEqBaseDistributionsExt = "Distributions" - DiffEqBaseEnzymeExt = ["ChainRulesCore", "Enzyme"] - DiffEqBaseGeneralizedGeneratedExt = "GeneralizedGenerated" - DiffEqBaseMPIExt = "MPI" - DiffEqBaseMeasurementsExt = "Measurements" - DiffEqBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" - DiffEqBaseReverseDiffExt = "ReverseDiff" - DiffEqBaseTrackerExt = "Tracker" - DiffEqBaseUnitfulExt = "Unitful" - - [deps.DiffEqBase.weakdeps] - CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" - MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" - Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" - MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" - ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" - Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" - Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" - -[[deps.DiffEqCallbacks]] -deps = ["DataStructures", "DiffEqBase", "ForwardDiff", "Functors", "LinearAlgebra", "Markdown", "NonlinearSolve", "Parameters", "RecipesBase", "RecursiveArrayTools", "SciMLBase", "StaticArraysCore"] -git-tree-sha1 = "c959cfd2657d16beada157a74d52269e8556500e" -uuid = "459566f4-90b8-5000-8ac3-15dfb0a30def" -version = "3.6.2" - - [deps.DiffEqCallbacks.weakdeps] - OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" - Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4" +version = "1.1.3" [[deps.DiffResults]] deps = ["StaticArraysCore"] @@ -466,51 +383,15 @@ git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" version = "1.15.1" -[[deps.DifferentiationInterface]] -deps = ["ADTypes", "Compat", "DocStringExtensions", "FillArrays", "LinearAlgebra", "PackageExtensionCompat", "SparseArrays", "SparseMatrixColorings"] -git-tree-sha1 = "695217e97ee1ce0248f4a56c14af88ba33c585fd" -uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" -version = "0.5.7" - - [deps.DifferentiationInterface.extensions] - DifferentiationInterfaceChainRulesCoreExt = "ChainRulesCore" - DifferentiationInterfaceDiffractorExt = "Diffractor" - DifferentiationInterfaceEnzymeExt = "Enzyme" - DifferentiationInterfaceFastDifferentiationExt = "FastDifferentiation" - DifferentiationInterfaceFiniteDiffExt = "FiniteDiff" - DifferentiationInterfaceFiniteDifferencesExt = "FiniteDifferences" - DifferentiationInterfaceForwardDiffExt = "ForwardDiff" - DifferentiationInterfacePolyesterForwardDiffExt = "PolyesterForwardDiff" - DifferentiationInterfaceReverseDiffExt = "ReverseDiff" - DifferentiationInterfaceSymbolicsExt = "Symbolics" - DifferentiationInterfaceTapirExt = "Tapir" - DifferentiationInterfaceTrackerExt = "Tracker" - DifferentiationInterfaceZygoteExt = ["Zygote", "ForwardDiff"] - - [deps.DifferentiationInterface.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Diffractor = "9f5e2b26-1114-432f-b630-d3fe2085c51c" - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - FastDifferentiation = "eb9bf01b-bf85-4b60-bf87-ee5de06c00be" - FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41" - FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" - ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" - PolyesterForwardDiff = "98d1487c-24ca-40b6-b7ab-df2af84e126b" - ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" - Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" - Tapir = "07d77754-e150-4737-8c94-cd238a1fb45b" - Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" - Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" - [[deps.Distributed]] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[deps.Distributions]] deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "9c405847cc7ecda2dc921ccf18b47ca150d7317e" +git-tree-sha1 = "0e0a1264b0942f1f3abb2b30891f2a590cc652ac" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.109" +version = "0.25.110" [deps.Distributions.extensions] DistributionsChainRulesCoreExt = "ChainRulesCore" @@ -543,12 +424,6 @@ deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" -[[deps.DualNumbers]] -deps = ["Calculus", "NaNMath", "SpecialFunctions"] -git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566" -uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" -version = "0.6.8" - [[deps.DynamicPolynomials]] deps = ["Future", "LinearAlgebra", "MultivariatePolynomials", "MutableArithmetics", "Pkg", "Reexport", "Test"] git-tree-sha1 = "30a1848c4f4fc35d1d4bbbd125650f6a11b5bc6c" @@ -572,15 +447,6 @@ git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.4" -[[deps.EnzymeCore]] -git-tree-sha1 = "3a3177ba05b4763234819060fb6c2e1613379ca6" -uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" -version = "0.7.6" -weakdeps = ["Adapt"] - - [deps.EnzymeCore.extensions] - AdaptExt = "Adapt" - [[deps.ExactPredicates]] deps = ["IntervalArithmetic", "Random", "StaticArrays"] git-tree-sha1 = "b3f2ff58735b5f024c392fde763f29b057e4b025" @@ -595,21 +461,21 @@ version = "2.6.2+0" [[deps.ExponentialAction]] deps = ["AbstractDifferentiation", "ChainRulesCore", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "10450af8987f4fc4c6ac4f22d0b35146730ad63d" +git-tree-sha1 = "f09115e954f495cab57771b30a31b06b04e79d31" uuid = "e24c0720-ea99-47e8-929e-571b494574d3" -version = "0.2.8" - -[[deps.ExponentialUtilities]] -deps = ["Adapt", "ArrayInterface", "GPUArraysCore", "GenericSchur", "LinearAlgebra", "PrecompileTools", "Printf", "SparseArrays", "libblastrampoline_jll"] -git-tree-sha1 = "8e18940a5ba7f4ddb41fe2b79b6acaac50880a86" -uuid = "d4d017d3-3776-5f7e-afef-a10c40355c18" -version = "1.26.1" +version = "0.2.9" [[deps.ExprTools]] git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" version = "0.1.10" +[[deps.Expronicon]] +deps = ["MLStyle", "Pkg", "TOML"] +git-tree-sha1 = "fc3951d4d398b5515f91d7fe5d45fc31dccb3c9b" +uuid = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636" +version = "0.8.5" + [[deps.Extents]] git-tree-sha1 = "94997910aca72897524d2237c41eb852153b0f65" uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" @@ -633,35 +499,6 @@ git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" version = "3.3.10+0" -[[deps.FastBroadcast]] -deps = ["ArrayInterface", "LinearAlgebra", "Polyester", "Static", "StaticArrayInterface", "StrideArraysCore"] -git-tree-sha1 = "bd19de6fe8a3b18888f35e79832f97544684caa7" -uuid = "7034ab61-46d4-4ed7-9d0f-46aef9175898" -version = "0.3.4" - -[[deps.FastClosures]] -git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" -uuid = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" -version = "0.3.2" - -[[deps.FastExpm]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "b4451ccbc6ff3d71670e90c05bf59feef8915b01" -uuid = "7868e603-8603-432e-a1a1-694bd70b01f2" -version = "1.1.0" - -[[deps.FastGaussQuadrature]] -deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" -uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "1.0.2" - -[[deps.FastLapackInterface]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "cbf5edddb61a43669710cbc2241bc08b36d9e660" -uuid = "29a986be-02c6-4525-aec4-84b980013641" -version = "2.0.4" - [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" @@ -685,9 +522,9 @@ uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[deps.FillArrays]] deps = ["LinearAlgebra"] -git-tree-sha1 = "0653c0a2396a6da5bc4766c43041ef5fd3efbe57" +git-tree-sha1 = "fd0002c0b5362d7eb952450ad5eb742443340d6e" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.11.0" +version = "1.12.0" weakdeps = ["PDMats", "SparseArrays", "Statistics"] [deps.FillArrays.extensions] @@ -695,22 +532,6 @@ weakdeps = ["PDMats", "SparseArrays", "Statistics"] FillArraysSparseArraysExt = "SparseArrays" FillArraysStatisticsExt = "Statistics" -[[deps.FiniteDiff]] -deps = ["ArrayInterface", "LinearAlgebra", "Requires", "Setfield", "SparseArrays"] -git-tree-sha1 = "2de436b72c3422940cbe1367611d137008af7ec3" -uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.23.1" - - [deps.FiniteDiff.extensions] - FiniteDiffBandedMatricesExt = "BandedMatrices" - FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" - FiniteDiffStaticArraysExt = "StaticArrays" - - [deps.FiniteDiff.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - [[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" @@ -773,12 +594,6 @@ git-tree-sha1 = "b104d487b34566608f8b4e1c39fb0b10aa279ff8" uuid = "77dc65aa-8811-40c2-897b-53d922fa7daf" version = "0.1.3" -[[deps.Functors]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "8a66c07630d6428eaab3506a0eabfcf4a9edea05" -uuid = "d9f16b24-f501-4c13-a1f2-28368ffc5196" -version = "0.4.11" - [[deps.Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" @@ -789,24 +604,6 @@ git-tree-sha1 = "ec632f177c0d990e64d955ccc1b8c04c485a0950" uuid = "46192b85-c4d5-4398-a991-12ede77f4527" version = "0.1.6" -[[deps.GSL]] -deps = ["GSL_jll", "Libdl", "Markdown"] -git-tree-sha1 = "3ebd07d519f5ec318d5bc1b4971e2472e14bd1f0" -uuid = "92c85e6c-cbff-5e0c-80f7-495c94daaecd" -version = "1.0.1" - -[[deps.GSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "56f1e2c9e083e0bb7cf9a7055c280beb08a924c0" -uuid = "1b77fbbe-d8ee-58f0-85f9-836ddc23a7a4" -version = "2.7.2+0" - -[[deps.GenericSchur]] -deps = ["LinearAlgebra", "Printf"] -git-tree-sha1 = "af49a0851f8113fcfae2ef5027c6d49d0acec39b" -uuid = "c145ed77-6b09-5dd9-b285-bf645a82121e" -version = "0.5.4" - [[deps.GeoInterface]] deps = ["Extents"] git-tree-sha1 = "9fff8990361d5127b770e3454488360443019bb3" @@ -843,12 +640,6 @@ git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" version = "1.3.14+0" -[[deps.Graphs]] -deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] -git-tree-sha1 = "ebd18c326fa6cee1efb7da9a3b45cf69da2ed4d9" -uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" -version = "1.11.2" - [[deps.GridLayoutBase]] deps = ["GeometryBasics", "InteractiveUtils", "Observables"] git-tree-sha1 = "6f93a83ca11346771a93bbde2bdad2f65b61498f" @@ -866,23 +657,17 @@ git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" version = "2.8.1+1" -[[deps.HostCPUFeatures]] -deps = ["BitTwiddlingConvenienceFunctions", "IfElse", "Libdl", "Static"] -git-tree-sha1 = "8e070b599339d622e9a081d17230d74a5c473293" -uuid = "3e5b6fbb-0976-4d2c-9146-d79de83f2fb0" -version = "0.1.17" - [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1d334207121865ac8c1c97eb7f42d0339e4635bf" +git-tree-sha1 = "5e19e1e4fa3e71b774ce746274364aef0234634e" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.0+0" +version = "2.11.1+0" [[deps.HypergeometricFunctions]] -deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "f218fe3736ddf977e0e772bc9a586b2383da2685" +deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] +git-tree-sha1 = "7c4195be1649ae622304031ed46a2f4df989f1eb" uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.23" +version = "0.3.24" [[deps.IJulia]] deps = ["Base64", "Conda", "Dates", "InteractiveUtils", "JSON", "Libdl", "Logging", "Markdown", "MbedTLS", "Pkg", "Printf", "REPL", "Random", "SoftGlobalScope", "Test", "UUIDs", "ZMQ"] @@ -931,12 +716,6 @@ git-tree-sha1 = "0936ba688c6d201805a83da835b55c61a180db52" uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" version = "3.1.11+0" -[[deps.IncompleteLU]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "6c676e79f98abb6d33fa28122cad099f1e464afe" -uuid = "40713840-3770-5561-ab4c-a76e7d0d7895" -version = "0.2.1" - [[deps.IndirectArrays]] git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" @@ -949,9 +728,9 @@ version = "0.1.5" [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "be50fe8df3acbffa0274a744f1a99d29c45a57f4" +git-tree-sha1 = "14eb2b542e748570b56446f4c50fbfb2306ebc45" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.1.0+0" +version = "2024.2.0+0" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -993,26 +772,26 @@ weakdeps = ["Random", "RecipesBase", "Statistics"] IntervalSetsStatisticsExt = "Statistics" [[deps.InverseFunctions]] -deps = ["Test"] -git-tree-sha1 = "e7cbed5032c4c397a6ac23d1493f3289e01231c4" +git-tree-sha1 = "2787db24f4e03daf859c6509ff87764e4182f7d1" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.14" -weakdeps = ["Dates"] +version = "0.1.16" +weakdeps = ["Dates", "Test"] [deps.InverseFunctions.extensions] - DatesExt = "Dates" + InverseFunctionsDatesExt = "Dates" + InverseFunctionsTestExt = "Test" [[deps.Ipopt]] deps = ["Ipopt_jll", "LinearAlgebra", "MathOptInterface", "OpenBLAS32_jll", "PrecompileTools"] -git-tree-sha1 = "344df015370d87d9ee6ff0be2f57d0ba81c4d863" +git-tree-sha1 = "92588db78296190d27668a560df3997719fc2a25" uuid = "b6b21f68-93f8-5de0-b562-5493be1d77c9" -version = "1.6.3" +version = "1.6.6" [[deps.Ipopt_jll]] deps = ["ASL_jll", "Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MUMPS_seq_jll", "SPRAL_jll", "libblastrampoline_jll"] -git-tree-sha1 = "546c40fd3718c65d48296dd6cec98af9904e3ca4" +git-tree-sha1 = "a0950d209a055b3adb6d29ade5cbdf005a6bd290" uuid = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7" -version = "300.1400.1400+0" +version = "300.1400.1600+0" [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" @@ -1036,10 +815,10 @@ uuid = "82899510-4779-5014-852e-03e436cf321d" version = "1.0.0" [[deps.JLD2]] -deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "Pkg", "PrecompileTools", "Reexport", "Requires", "TranscodingStreams", "UUIDs", "Unicode"] -git-tree-sha1 = "84642bc18a79d715b39d3724b03cbdd2e7d48c62" +deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "PrecompileTools", "Reexport", "Requires", "TranscodingStreams", "UUIDs", "Unicode"] +git-tree-sha1 = "67d4690d32c22e28818a434b293a374cc78473d3" uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" -version = "0.4.49" +version = "0.4.51" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] @@ -1065,24 +844,12 @@ git-tree-sha1 = "c84a835e1a09b289ffcd2271bf2a337bbdda6637" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" version = "3.0.3+0" -[[deps.KLU]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] -git-tree-sha1 = "07649c499349dad9f08dde4243a4c597064663e9" -uuid = "ef3ab10e-7fda-4108-b977-705223b18434" -version = "0.6.0" - [[deps.KernelDensity]] deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] git-tree-sha1 = "7d703202e65efa1369de1279c162b915e245eed1" uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" version = "0.6.9" -[[deps.Krylov]] -deps = ["LinearAlgebra", "Printf", "SparseArrays"] -git-tree-sha1 = "267dad6b4b7b5d529c76d40ff48d33f7e94cb834" -uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" -version = "0.9.6" - [[deps.LAME_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" @@ -1091,18 +858,9 @@ version = "3.100.2+0" [[deps.LLVMOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "d986ce2d884d49126836ea94ed5bfb0f12679713" +git-tree-sha1 = "e16271d212accd09d52ee0ae98956b8a05c4b626" uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "15.0.7+0" - -[[deps.LRUCache]] -git-tree-sha1 = "b3cc6698599b10e652832c2f23db3cab99d51b59" -uuid = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637" -version = "1.6.1" -weakdeps = ["Serialization"] - - [deps.LRUCache.extensions] - SerializationExt = ["Serialization"] +version = "17.0.6+0" [[deps.LZO_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1128,42 +886,20 @@ version = "0.4.6" [[deps.Latexify]] deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] -git-tree-sha1 = "5b0d630f3020b82c0775a51d05895852f8506f50" +git-tree-sha1 = "ce5f5621cac23a86011836badfedf664a612cee4" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.4" +version = "0.16.5" [deps.Latexify.extensions] DataFramesExt = "DataFrames" + SparseArraysExt = "SparseArrays" SymEngineExt = "SymEngine" [deps.Latexify.weakdeps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" -[[deps.LayoutPointers]] -deps = ["ArrayInterface", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface"] -git-tree-sha1 = "a9eaadb366f5493a5654e843864c13d8b107548c" -uuid = "10f19ff3-798f-405d-979b-55457f8fc047" -version = "0.1.17" - -[[deps.LazyArrays]] -deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra", "MacroTools", "SparseArrays"] -git-tree-sha1 = "35d73607f9e74c18db8a434b4886a479faa8e6fa" -uuid = "5078a376-72f3-5289-bfd5-ec5146d43c02" -version = "2.1.4" - - [deps.LazyArrays.extensions] - LazyArraysBandedMatricesExt = "BandedMatrices" - LazyArraysBlockArraysExt = "BlockArrays" - LazyArraysBlockBandedMatricesExt = "BlockBandedMatrices" - LazyArraysStaticArraysExt = "StaticArrays" - - [deps.LazyArrays.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" - BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - [[deps.LazyArtifacts]] deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" @@ -1236,65 +972,10 @@ git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" version = "2.40.1+0" -[[deps.LineSearches]] -deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "Printf"] -git-tree-sha1 = "7bbea35cec17305fc70a0e5b4641477dc0789d9d" -uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" -version = "7.2.0" - [[deps.LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -[[deps.LinearMaps]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" -uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.11.3" -weakdeps = ["ChainRulesCore", "SparseArrays", "Statistics"] - - [deps.LinearMaps.extensions] - LinearMapsChainRulesCoreExt = "ChainRulesCore" - LinearMapsSparseArraysExt = "SparseArrays" - LinearMapsStatisticsExt = "Statistics" - -[[deps.LinearSolve]] -deps = ["ArrayInterface", "ChainRulesCore", "ConcreteStructs", "DocStringExtensions", "EnumX", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "LazyArrays", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "b2e2dba60642e07c062eb3143770d7e234316772" -uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "2.30.2" - - [deps.LinearSolve.extensions] - LinearSolveBandedMatricesExt = "BandedMatrices" - LinearSolveBlockDiagonalsExt = "BlockDiagonals" - LinearSolveCUDAExt = "CUDA" - LinearSolveCUDSSExt = "CUDSS" - LinearSolveEnzymeExt = ["Enzyme", "EnzymeCore"] - LinearSolveFastAlmostBandedMatricesExt = ["FastAlmostBandedMatrices"] - LinearSolveHYPREExt = "HYPRE" - LinearSolveIterativeSolversExt = "IterativeSolvers" - LinearSolveKernelAbstractionsExt = "KernelAbstractions" - LinearSolveKrylovKitExt = "KrylovKit" - LinearSolveMetalExt = "Metal" - LinearSolvePardisoExt = "Pardiso" - LinearSolveRecursiveArrayToolsExt = "RecursiveArrayTools" - - [deps.LinearSolve.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" - CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" - CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" - FastAlmostBandedMatrices = "9d29842c-ecb8-4973-b1e9-a27b1157504e" - HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771" - IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" - KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" - KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" - Metal = "dde4c033-4e86-420c-a63e-0dd931031962" - Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" - RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" - [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" @@ -1314,17 +995,6 @@ version = "0.3.28" [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -[[deps.LoopVectorization]] -deps = ["ArrayInterface", "CPUSummary", "CloseOpenIntervals", "DocStringExtensions", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "PrecompileTools", "SIMDTypes", "SLEEFPirates", "Static", "StaticArrayInterface", "ThreadingUtilities", "UnPack", "VectorizationBase"] -git-tree-sha1 = "8084c25a250e00ae427a379a5b607e7aed96a2dd" -uuid = "bdcacae8-1622-11e9-2a5c-532679323890" -version = "0.12.171" -weakdeps = ["ChainRulesCore", "ForwardDiff", "SpecialFunctions"] - - [deps.LoopVectorization.extensions] - ForwardDiffExt = ["ChainRulesCore", "ForwardDiff"] - SpecialFunctionsExt = "SpecialFunctions" - [[deps.METIS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" @@ -1333,15 +1003,20 @@ version = "5.1.2+0" [[deps.MKL_jll]] deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "80b2833b56d466b3858d565adcd16a4a05f2089b" +git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.1.0+0" +version = "2024.2.0+0" + +[[deps.MLStyle]] +git-tree-sha1 = "bc38dff0548128765760c79eb7388a4b37fae2c8" +uuid = "d8e11817-5142-5d16-987a-aa16d5891078" +version = "0.4.17" [[deps.MUMPS_seq_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "840b83c65b27e308095c139a457373850b2f5977" +git-tree-sha1 = "85047ac569761e3387717480a38a61d2a67df45c" uuid = "d7ed1dd3-d0ae-5e8e-bfb4-87a502085b8d" -version = "500.600.201+0" +version = "500.700.300+0" [[deps.MacroTools]] deps = ["Markdown", "Random"] @@ -1361,11 +1036,6 @@ git-tree-sha1 = "248b7a4be0f92b497f7a331aed02c1e9a878f46b" uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" version = "0.7.3" -[[deps.ManualMemory]] -git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" -uuid = "d125e4d3-2237-4719-b19c-fa641b8a4667" -version = "0.1.8" - [[deps.MappedArrays]] git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" @@ -1377,9 +1047,9 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[deps.MathOptInterface]] deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MutableArithmetics", "NaNMath", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays", "SpecialFunctions", "Test", "Unicode"] -git-tree-sha1 = "91b08d27a27d83cf1e63e50837403e7f53a0fd74" +git-tree-sha1 = "c0fe113e9c72aa0c9a185fd3c5ca1daa51de1486" uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" -version = "1.31.0" +version = "1.31.1" [[deps.MathTeXEngine]] deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] @@ -1387,12 +1057,6 @@ git-tree-sha1 = "96ca8a313eb6437db5ffe946c457a401bbb8ce1d" uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" version = "0.5.7" -[[deps.MaybeInplace]] -deps = ["ArrayInterface", "LinearAlgebra", "MacroTools", "SparseArrays"] -git-tree-sha1 = "1b9e613f2ca3b6cdcbfe36381e17ca2b66d4b3a1" -uuid = "bb5d69b7-63fc-4a16-80bd-7e42200c7bdb" -version = "0.1.3" - [[deps.MbedTLS]] deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf" @@ -1423,11 +1087,6 @@ version = "0.3.4" uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2023.1.10" -[[deps.MuladdMacro]] -git-tree-sha1 = "cac9cc5499c25554cba55cd3c30543cff5ca4fab" -uuid = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" -version = "0.2.4" - [[deps.MultivariatePolynomials]] deps = ["ChainRulesCore", "DataStructures", "LinearAlgebra", "MutableArithmetics"] git-tree-sha1 = "5c1d1d9361e1417e5a065e1f84dc3686cbdaea21" @@ -1436,15 +1095,9 @@ version = "0.5.6" [[deps.MutableArithmetics]] deps = ["LinearAlgebra", "SparseArrays", "Test"] -git-tree-sha1 = "898c56fbf8bf71afb0c02146ef26f3a454e88873" +git-tree-sha1 = "d0a6b1096b584a2b88efb70a92f8cb8c881eb38a" uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" -version = "1.4.5" - -[[deps.NLSolversBase]] -deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] -git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" -uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" -version = "7.8.3" +version = "1.4.6" [[deps.NaNMath]] deps = ["OpenLibm_jll"] @@ -1454,11 +1107,11 @@ version = "1.0.2" [[deps.NamedTrajectories]] deps = ["CairoMakie", "JLD2", "LaTeXStrings", "Latexify", "OrderedCollections", "Random", "Reexport", "Unidecode"] -git-tree-sha1 = "185543e9867e5dbec0e8ce3c82f6de31e278f499" +git-tree-sha1 = "94fa6864fcf891e7214a1dd73c773d325556de28" repo-rev = "main" -repo-url = "https://github.com/aarontrowbridge/NamedTrajectories.jl.git" +repo-url = "https://github.com/kestrelquantum/NamedTrajectories.jl.git" uuid = "538bc3a1-5ab9-4fc3-b776-35ca1e893e08" -version = "0.1.6" +version = "0.2.0" [[deps.Netpbm]] deps = ["FileIO", "ImageCore", "ImageMetadata"] @@ -1470,38 +1123,6 @@ version = "1.1.1" uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" -[[deps.NonlinearSolve]] -deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "FastBroadcast", "FastClosures", "FiniteDiff", "ForwardDiff", "LazyArrays", "LineSearches", "LinearAlgebra", "LinearSolve", "MaybeInplace", "PrecompileTools", "Preferences", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] -git-tree-sha1 = "3adb1e5945b5a6b1eaee754077f25ccc402edd7f" -uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" -version = "3.13.1" - - [deps.NonlinearSolve.extensions] - NonlinearSolveBandedMatricesExt = "BandedMatrices" - NonlinearSolveFastLevenbergMarquardtExt = "FastLevenbergMarquardt" - NonlinearSolveFixedPointAccelerationExt = "FixedPointAcceleration" - NonlinearSolveLeastSquaresOptimExt = "LeastSquaresOptim" - NonlinearSolveMINPACKExt = "MINPACK" - NonlinearSolveNLSolversExt = "NLSolvers" - NonlinearSolveNLsolveExt = "NLsolve" - NonlinearSolveSIAMFANLEquationsExt = "SIAMFANLEquations" - NonlinearSolveSpeedMappingExt = "SpeedMapping" - NonlinearSolveSymbolicsExt = "Symbolics" - NonlinearSolveZygoteExt = "Zygote" - - [deps.NonlinearSolve.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - FastLevenbergMarquardt = "7a0df574-e128-4d35-8cbd-3d84502bf7ce" - FixedPointAcceleration = "817d07cb-a79a-5c30-9a31-890123675176" - LeastSquaresOptim = "0fc2ff8b-aaa3-5acd-a817-1944a5e08891" - MINPACK = "4854310b-de5a-5eb6-a2a5-c1dee2bd17f9" - NLSolvers = "337daf1e-9722-11e9-073e-8b9effe078ba" - NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" - SIAMFANLEquations = "084e46ad-d928-497d-ad5e-07fa361a48c4" - SpeedMapping = "f1835b91-879b-4a3f-a438-e4baacf14412" - Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" - Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" - [[deps.Observables]] git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" uuid = "510215fc-4207-5dde-b226-833fc4488ee2" @@ -1573,12 +1194,6 @@ git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" version = "1.6.3" -[[deps.OrdinaryDiffEq]] -deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "IfElse", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "6ef13f8b23af28ee2d98226653d8382ab79287ea" -uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -version = "6.85.0" - [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" @@ -1596,12 +1211,6 @@ git-tree-sha1 = "67186a2bc9a90f9f85ff3cc8277868961fb57cbd" uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" version = "0.4.3" -[[deps.PackageExtensionCompat]] -git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" -uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" -version = "1.0.2" -weakdeps = ["Requires", "TOML"] - [[deps.Packing]] deps = ["GeometryBasics"] git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501" @@ -1620,12 +1229,6 @@ git-tree-sha1 = "cb5a2ab6763464ae0f19c86c56c63d4a2b0f5bda" uuid = "36c8627f-9965-5494-a995-c6b170f724f3" version = "1.52.2+0" -[[deps.Parameters]] -deps = ["OrderedCollections", "UnPack"] -git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.3" - [[deps.Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" @@ -1655,18 +1258,6 @@ git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" version = "1.4.1" -[[deps.Polyester]] -deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Requires", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] -git-tree-sha1 = "9ff799e8fb8ed6717710feee3be3bc20645daa97" -uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" -version = "0.7.15" - -[[deps.PolyesterWeave]] -deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] -git-tree-sha1 = "645bed98cd47f72f67316fd42fc47dee771aefcd" -uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" -version = "0.2.2" - [[deps.PolygonOps]] git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" uuid = "647866c9-e3ac-4575-94e7-e3d426903924" @@ -1674,9 +1265,9 @@ version = "0.1.2" [[deps.PreallocationTools]] deps = ["Adapt", "ArrayInterface", "ForwardDiff"] -git-tree-sha1 = "406c29a7f46706d379a3bce45671b4e3a39ddfbc" +git-tree-sha1 = "6c62ce45f268f3f958821a1e5192cf91c75ae89c" uuid = "d236fae5-4411-538c-8e31-a6e3d9e00b46" -version = "0.4.22" +version = "0.4.24" [deps.PreallocationTools.extensions] PreallocationToolsReverseDiffExt = "ReverseDiff" @@ -1706,9 +1297,9 @@ uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" [[deps.ProgressMeter]] deps = ["Distributed", "Printf"] -git-tree-sha1 = "763a8ceb07833dd51bb9e3bbca372de32c0605ad" +git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4" uuid = "92933f4c-e287-5a05-a399-4b506db050ca" -version = "1.10.0" +version = "1.10.2" [[deps.PtrArrays]] git-tree-sha1 = "f011fbb92c4d401059b2212c05c0601b70f8b759" @@ -1723,33 +1314,9 @@ version = "1.0.0" [[deps.QuadGK]] deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "9b23c31e76e333e6fb4c1595ae6afa74966a729e" +git-tree-sha1 = "e237232771fdafbae3db5c31275303e056afaa9f" uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.9.4" - -[[deps.QuantumInterface]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "5e4f1f1a93d2f7f8f837af9122342e43b1fe5199" -uuid = "5717a53b-5d69-4fa3-b976-0bf2f97ca1e5" -version = "0.3.4" - -[[deps.QuantumOpticsBase]] -deps = ["Adapt", "FFTW", "FastExpm", "FastGaussQuadrature", "FillArrays", "LRUCache", "LinearAlgebra", "QuantumInterface", "Random", "RandomMatrices", "SparseArrays", "Strided", "UnsafeArrays"] -git-tree-sha1 = "f0f17dce78cbcd193165b568480ae678745354a4" -uuid = "4f57444f-1401-5e15-980d-4471b28d5678" -version = "0.5.0" - -[[deps.QuantumToolbox]] -deps = ["ArrayInterface", "DiffEqCallbacks", "FFTW", "Graphs", "IncompleteLU", "LinearAlgebra", "LinearMaps", "LinearSolve", "OrdinaryDiffEq", "Pkg", "Random", "Reexport", "SparseArrays", "SpecialFunctions"] -git-tree-sha1 = "fade211aa50250c43f1d5af18dae8d0dcf6f1e8f" -uuid = "6c2fb7c5-b903-41d2-bc5e-5a7c320b9fab" -version = "0.11.2" - - [deps.QuantumToolbox.extensions] - QuantumToolboxCUDAExt = "CUDA" - - [deps.QuantumToolbox.weakdeps] - CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +version = "2.10.1" [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] @@ -1759,12 +1326,6 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -[[deps.RandomMatrices]] -deps = ["Combinatorics", "Distributions", "FastGaussQuadrature", "GSL", "LinearAlgebra", "Random", "SpecialFunctions", "Test"] -git-tree-sha1 = "ce39acdf57c11919b957afa1974fe774b8f94fcc" -uuid = "2576dda1-a324-5b11-aa66-c48ed7e3c618" -version = "0.5.4" - [[deps.RangeArrays]] git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d" @@ -1787,10 +1348,10 @@ uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" version = "1.3.4" [[deps.RecursiveArrayTools]] -deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "SparseArrays", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "b450d967a770fb13d0e26358f58375e20361cf9c" +deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] +git-tree-sha1 = "b034171b93aebc81b3e1890a036d13a9c4a9e3e0" uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" -version = "3.26.0" +version = "3.27.0" [deps.RecursiveArrayTools.extensions] RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" @@ -1798,6 +1359,7 @@ version = "3.26.0" RecursiveArrayToolsMeasurementsExt = "Measurements" RecursiveArrayToolsMonteCarloMeasurementsExt = "MonteCarloMeasurements" RecursiveArrayToolsReverseDiffExt = ["ReverseDiff", "Zygote"] + RecursiveArrayToolsSparseArraysExt = ["SparseArrays"] RecursiveArrayToolsTrackerExt = "Tracker" RecursiveArrayToolsZygoteExt = "Zygote" @@ -1807,15 +1369,10 @@ version = "3.26.0" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" -[[deps.RecursiveFactorization]] -deps = ["LinearAlgebra", "LoopVectorization", "Polyester", "PrecompileTools", "StrideArraysCore", "TriangularSolve"] -git-tree-sha1 = "6db1a75507051bc18bfa131fbc7c3f169cc4b2f6" -uuid = "f2c3362d-daeb-58d1-803e-2bc74f2840b4" -version = "0.2.23" - [[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" @@ -1841,9 +1398,9 @@ version = "0.7.1" [[deps.Rmath_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "d483cd324ce5cf5d61b77930f0bbd6cb61927d21" +git-tree-sha1 = "e60724fd3beea548353984dc61c943ecddb0e29a" uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.4.2+0" +version = "0.4.3+0" [[deps.RoundingEmulator]] git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" @@ -1866,28 +1423,17 @@ git-tree-sha1 = "2803cab51702db743f3fda07dd1745aadfbf43bd" uuid = "fdea26ae-647d-5447-a871-4b548cad5224" version = "3.5.0" -[[deps.SIMDTypes]] -git-tree-sha1 = "330289636fb8107c5f32088d2741e9fd7a061a5c" -uuid = "94e857df-77ce-4151-89e5-788b33177be4" -version = "0.1.0" - -[[deps.SLEEFPirates]] -deps = ["IfElse", "Static", "VectorizationBase"] -git-tree-sha1 = "456f610ca2fbd1c14f5fcf31c6bfadc55e7d66e0" -uuid = "476501e8-09a2-5ece-8869-fb82de89a1fa" -version = "0.6.43" - [[deps.SPRAL_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "34b9dacd687cace8aa4d550e3e9bb8615f1a61e9" +git-tree-sha1 = "11f3da4b25efacd1cec8e263421f2a9003a5e8e0" uuid = "319450e9-13b8-58e8-aa9f-8fd1420848ab" -version = "2024.1.18+0" +version = "2024.5.8+0" [[deps.SciMLBase]] -deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "281e82f2ae2b73262fed9e7a518711eb7feb7e59" +deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "Expronicon", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] +git-tree-sha1 = "5123ca064567e81c31fb3acdf15d2c9459bb7cc3" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.42.0" +version = "2.50.0" [deps.SciMLBase.extensions] SciMLBaseChainRulesCoreExt = "ChainRulesCore" @@ -1909,16 +1455,17 @@ version = "2.42.0" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SciMLOperators]] -deps = ["ArrayInterface", "DocStringExtensions", "LinearAlgebra", "MacroTools", "Setfield", "SparseArrays", "StaticArraysCore"] -git-tree-sha1 = "10499f619ef6e890f3f4a38914481cc868689cd5" +deps = ["ArrayInterface", "DocStringExtensions", "LinearAlgebra", "MacroTools", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "23b02c588ac9a17ecb276cc62ab37f3e4fe37b32" uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" -version = "0.3.8" +version = "0.3.9" +weakdeps = ["SparseArrays"] [[deps.SciMLStructures]] deps = ["ArrayInterface"] -git-tree-sha1 = "cfdd1200d150df1d3c055cc72ee6850742e982d7" +git-tree-sha1 = "20ad3e7c137156c50c93c888d0f2bc5b7883c729" uuid = "53ae85a6-f571-4167-b2af-e1d143709226" -version = "1.4.1" +version = "1.4.2" [[deps.Scratch]] deps = ["Dates"] @@ -1957,35 +1504,12 @@ git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" version = "0.4.0" -[[deps.SimpleNonlinearSolve]] -deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "DiffResults", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "MaybeInplace", "PrecompileTools", "Reexport", "SciMLBase", "Setfield", "StaticArraysCore"] -git-tree-sha1 = "58b144f34e44252b2de0acb5a9dbbb7ea5cd75d7" -uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "1.10.1" - - [deps.SimpleNonlinearSolve.extensions] - SimpleNonlinearSolveChainRulesCoreExt = "ChainRulesCore" - SimpleNonlinearSolveReverseDiffExt = "ReverseDiff" - SimpleNonlinearSolveTrackerExt = "Tracker" - SimpleNonlinearSolveZygoteExt = "Zygote" - - [deps.SimpleNonlinearSolve.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" - Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" - Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" - [[deps.SimpleTraits]] deps = ["InteractiveUtils", "MacroTools"] git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" version = "0.9.4" -[[deps.SimpleUnPack]] -git-tree-sha1 = "58e6353e72cde29b90a69527e56df1b5c3d8c437" -uuid = "ce78b400-467f-4804-87d8-8f486da07d0a" -version = "1.1.0" - [[deps.Sixel]] deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"] git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6" @@ -2012,38 +1536,6 @@ deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" version = "1.10.0" -[[deps.SparseDiffTools]] -deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Random", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "Tricks", "UnPack", "VertexSafeGraphs"] -git-tree-sha1 = "469f51f8c4741ce944be2c0b65423b518b1405b0" -uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "2.19.0" - - [deps.SparseDiffTools.extensions] - SparseDiffToolsEnzymeExt = "Enzyme" - SparseDiffToolsPolyesterExt = "Polyester" - SparseDiffToolsPolyesterForwardDiffExt = "PolyesterForwardDiff" - SparseDiffToolsSymbolicsExt = "Symbolics" - SparseDiffToolsZygoteExt = "Zygote" - - [deps.SparseDiffTools.weakdeps] - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" - PolyesterForwardDiff = "98d1487c-24ca-40b6-b7ab-df2af84e126b" - Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" - Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" - -[[deps.SparseMatrixColorings]] -deps = ["ADTypes", "Compat", "DocStringExtensions", "LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "eed2446b3c3dd58f6ded3168998b8b2cb3fc9229" -uuid = "0a514795-09f3-496d-8182-132a7b665d35" -version = "0.3.3" - -[[deps.Sparspak]] -deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] -git-tree-sha1 = "342cf4b449c299d8d1ceaf00b7a49f4fbc7940e7" -uuid = "e56a9233-b9d6-4f03-8d0f-1825330902ac" -version = "0.3.9" - [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" @@ -2060,23 +1552,6 @@ git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15" version = "0.1.1" -[[deps.Static]] -deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools"] -git-tree-sha1 = "0bbff21027dd8a107551847528127b62a35f7594" -uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" -version = "1.1.0" - -[[deps.StaticArrayInterface]] -deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "Requires", "SparseArrays", "Static", "SuiteSparse"] -git-tree-sha1 = "8963e5a083c837531298fc41599182a759a87a6d" -uuid = "0d7ed370-da01-4f52-bd93-41d350b8b718" -version = "1.5.1" -weakdeps = ["OffsetArrays", "StaticArrays"] - - [deps.StaticArrayInterface.extensions] - StaticArrayInterfaceOffsetArraysExt = "OffsetArrays" - StaticArrayInterfaceStaticArraysExt = "StaticArrays" - [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" @@ -2121,30 +1596,6 @@ weakdeps = ["ChainRulesCore", "InverseFunctions"] StatsFunsChainRulesCoreExt = "ChainRulesCore" StatsFunsInverseFunctionsExt = "InverseFunctions" -[[deps.StrideArraysCore]] -deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] -git-tree-sha1 = "f35f6ab602df8413a50c4a25ca14de821e8605fb" -uuid = "7792a7ef-975c-4747-a70f-980b88e8d1da" -version = "0.5.7" - -[[deps.Strided]] -deps = ["LinearAlgebra", "StridedViews", "TupleTools"] -git-tree-sha1 = "bd9bd1c70cfc115cc3a30213fc725125a6b43652" -uuid = "5e0ebb24-38b0-5f93-81fe-25c709ecae67" -version = "2.1.0" - -[[deps.StridedViews]] -deps = ["LinearAlgebra", "PackageExtensionCompat"] -git-tree-sha1 = "2917996ce0fa6b8a3a85240a5e9ff930e2aeaa43" -uuid = "4db3bf67-4bd7-4b4e-b153-31dc3fb37143" -version = "0.3.1" - - [deps.StridedViews.extensions] - StridedViewsCUDAExt = "CUDA" - - [deps.StridedViews.weakdeps] - CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" - [[deps.StructArrays]] deps = ["ConstructionBase", "DataAPI", "Tables"] git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be" @@ -2169,35 +1620,37 @@ version = "7.2.1+1" [[deps.SymbolicIndexingInterface]] deps = ["Accessors", "ArrayInterface", "RuntimeGeneratedFunctions", "StaticArraysCore"] -git-tree-sha1 = "9c490ee01823dc443da25bf9225827e3cdd2d7e9" +git-tree-sha1 = "c9fce29fb41a10677e24f74421ebe31220b81ad0" uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" -version = "0.3.26" +version = "0.3.28" [[deps.SymbolicLimits]] deps = ["SymbolicUtils"] -git-tree-sha1 = "fb099adbd7504f1e68b4512828e9d94197a8b889" +git-tree-sha1 = "fabf4650afe966a2ba646cabd924c3fd43577fc3" uuid = "19f23fe9-fdab-4a78-91af-e7b7767979c3" -version = "0.2.1" +version = "0.2.2" [[deps.SymbolicUtils]] deps = ["AbstractTrees", "Bijections", "ChainRulesCore", "Combinatorics", "ConstructionBase", "DataStructures", "DocStringExtensions", "DynamicPolynomials", "IfElse", "LabelledArrays", "LinearAlgebra", "MultivariatePolynomials", "NaNMath", "Setfield", "SparseArrays", "SpecialFunctions", "StaticArrays", "SymbolicIndexingInterface", "TermInterface", "TimerOutputs", "Unityper"] -git-tree-sha1 = "cc049913a449be8c7ac00979831f26891e01add2" +git-tree-sha1 = "d00729521f49d96afd3fcddb437141eb71222886" uuid = "d1185830-fcd6-423d-90d6-eec64667417b" -version = "2.1.0" +version = "3.2.0" [[deps.Symbolics]] -deps = ["ADTypes", "ArrayInterface", "Bijections", "CommonWorldInvalidations", "ConstructionBase", "DataStructures", "DiffRules", "Distributions", "DocStringExtensions", "DomainSets", "DynamicPolynomials", "ForwardDiff", "IfElse", "LaTeXStrings", "LambertW", "Latexify", "Libdl", "LinearAlgebra", "LogExpFunctions", "MacroTools", "Markdown", "NaNMath", "PrecompileTools", "RecipesBase", "Reexport", "Requires", "RuntimeGeneratedFunctions", "SciMLBase", "Setfield", "SparseArrays", "SpecialFunctions", "StaticArraysCore", "SymbolicIndexingInterface", "SymbolicLimits", "SymbolicUtils", "TermInterface"] -git-tree-sha1 = "8539a734b448ca27707709c62420b1bf115ed7d9" +deps = ["ADTypes", "ArrayInterface", "Bijections", "CommonWorldInvalidations", "ConstructionBase", "DataStructures", "DiffRules", "Distributions", "DocStringExtensions", "DomainSets", "DynamicPolynomials", "IfElse", "LaTeXStrings", "LambertW", "Latexify", "Libdl", "LinearAlgebra", "LogExpFunctions", "MacroTools", "Markdown", "NaNMath", "PrecompileTools", "RecipesBase", "Reexport", "RuntimeGeneratedFunctions", "SciMLBase", "Setfield", "SparseArrays", "SpecialFunctions", "StaticArraysCore", "SymbolicIndexingInterface", "SymbolicLimits", "SymbolicUtils", "TermInterface"] +git-tree-sha1 = "4224422f6a5452b1accaec15a65a5ce3c2ca4600" uuid = "0c5d862f-8b57-4792-8d23-62f2024744c7" -version = "5.33.0" +version = "6.2.0" [deps.Symbolics.extensions] + SymbolicsForwardDiffExt = "ForwardDiff" SymbolicsGroebnerExt = "Groebner" SymbolicsLuxCoreExt = "LuxCore" - SymbolicsPreallocationToolsExt = "PreallocationTools" + SymbolicsPreallocationToolsExt = ["PreallocationTools", "ForwardDiff"] SymbolicsSymPyExt = "SymPy" [deps.Symbolics.weakdeps] + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Groebner = "0b43b601-686d-58a3-8a1c-6623616c7cd4" LuxCore = "bb33d45b-7691-41d6-9220-0943567d0623" PreallocationTools = "d236fae5-4411-538c-8e31-a6e3d9e00b46" @@ -2215,10 +1668,10 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" [[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.11.1" +version = "1.12.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -2232,9 +1685,9 @@ uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" version = "0.1.1" [[deps.TermInterface]] -git-tree-sha1 = "6f0cee95e74d1f6891ba6b35b8b219fd3d11b567" +git-tree-sha1 = "d673e0aca9e46a2f63720201f55cc7b3e7169b16" uuid = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c" -version = "0.4.1" +version = "2.0.0" [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] @@ -2251,12 +1704,6 @@ git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" version = "0.1.1" -[[deps.ThreadingUtilities]] -deps = ["ManualMemory"] -git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" -uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" -version = "0.5.2" - [[deps.TiffImages]] deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"] git-tree-sha1 = "bc7fd5c91041f44636b2c134041f7e5263ce58ae" @@ -2275,50 +1722,19 @@ uuid = "6dad8b7f-dd9a-4c28-9b70-85b9a079bfc8" version = "0.1.0" [[deps.TranscodingStreams]] -git-tree-sha1 = "60df3f8126263c0d6b357b9a1017bb94f53e3582" +git-tree-sha1 = "e84b3a11b9bece70d14cce63406bbc79ed3464d2" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.11.0" -weakdeps = ["Random", "Test"] - - [deps.TranscodingStreams.extensions] - TestExt = ["Test", "Random"] - -[[deps.TriangularSolve]] -deps = ["CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "LoopVectorization", "Polyester", "Static", "VectorizationBase"] -git-tree-sha1 = "be986ad9dac14888ba338c2554dcfec6939e1393" -uuid = "d5829a12-d9aa-46ab-831f-fb7c9ab06edf" -version = "0.2.1" - -[[deps.Tricks]] -git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f" -uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" -version = "0.1.8" +version = "0.11.2" [[deps.TriplotBase]] git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" uuid = "981d1d27-644d-49a2-9326-4793e63143c3" version = "0.1.0" -[[deps.TruncatedStacktraces]] -deps = ["InteractiveUtils", "MacroTools", "Preferences"] -git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" -uuid = "781d530d-4396-4725-bb49-402e4bee1e77" -version = "1.4.0" - -[[deps.TupleTools]] -git-tree-sha1 = "41d61b1c545b06279871ef1a4b5fcb2cac2191cd" -uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" -version = "1.5.0" - [[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -[[deps.UnPack]] -git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" -uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" -version = "1.0.2" - [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" @@ -2340,28 +1756,11 @@ git-tree-sha1 = "25008b734a03736c41e2a7dc314ecb95bd6bbdb0" uuid = "a7c27f48-0311-42f6-a7f8-2c11e75eb415" version = "0.1.6" -[[deps.UnsafeArrays]] -git-tree-sha1 = "e7f1c67ba99ac6df440de191fa4d5cbfcbdddcd1" -uuid = "c4a57d5a-5b31-53a6-b365-19f8c011fbd6" -version = "1.0.5" - -[[deps.VectorizationBase]] -deps = ["ArrayInterface", "CPUSummary", "HostCPUFeatures", "IfElse", "LayoutPointers", "Libdl", "LinearAlgebra", "SIMDTypes", "Static", "StaticArrayInterface"] -git-tree-sha1 = "e7f5b81c65eb858bed630fe006837b935518aca5" -uuid = "3d5dd08c-fd9d-11e8-17fa-ed2836048c2f" -version = "0.21.70" - [[deps.VersionParsing]] git-tree-sha1 = "58d6e80b4ee071f5efd07fda82cb9fbe17200868" uuid = "81def892-9a0e-5fdd-b105-ffc91e053289" version = "1.3.0" -[[deps.VertexSafeGraphs]] -deps = ["Graphs"] -git-tree-sha1 = "8351f8d73d7e880bfc042a8b6922684ebeafb35c" -uuid = "19fa3120-7c27-5ec5-8db8-b0b0aa330d6f" -version = "0.2.0" - [[deps.WoodburyMatrices]] deps = ["LinearAlgebra", "SparseArrays"] git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511" @@ -2430,9 +1829,9 @@ version = "1.5.0+0" [[deps.ZMQ]] deps = ["FileWatching", "PrecompileTools", "Sockets", "ZeroMQ_jll"] -git-tree-sha1 = "ad6944a6b3dfd8e95920e48355d3baf510319262" +git-tree-sha1 = "18cfd00df3cbbebf8ea4ec7ea6bbceb3af716bd0" uuid = "c2297ded-f4af-51ae-bb23-16f91089e4e1" -version = "1.2.6" +version = "1.3.0" [[deps.ZeroMQ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "libsodium_jll"] @@ -2494,9 +1893,9 @@ version = "1.0.20+0" [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" +git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.7+1" +version = "1.3.7+2" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] diff --git a/Project.toml b/Project.toml index 9838453b..e82b01b5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "QuantumCollocation" uuid = "0dc23a59-5ffb-49af-b6bd-932a8ae77adf" authors = ["Aaron Trowbridge and contributors"] -version = "0.3.0" +version = "0.2.0" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" @@ -17,9 +17,7 @@ Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" NamedTrajectories = "538bc3a1-5ab9-4fc3-b776-35ca1e893e08" -QuantumInterface = "5717a53b-5d69-4fa3-b976-0bf2f97ca1e5" -QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678" -QuantumToolbox = "6c2fb7c5-b903-41d2-bc5e-5a7c320b9fab" +ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -31,6 +29,7 @@ TrajectoryIndexingUtils = "6dad8b7f-dd9a-4c28-9b70-85b9a079bfc8" [compat] BenchmarkTools = "1.3" CairoMakie = "0.10, 0.11, 0.12" +Distributions = "≥0.25" Einsum = "0.4" ExponentialAction = "0.2" ForwardDiff = "0.10" @@ -38,9 +37,11 @@ IJulia = "1.24" Ipopt = "1.4" JLD2 = "0.4" MathOptInterface = "1.20" -NamedTrajectories = "0.1" +NamedTrajectories = "0.2.0" +ProgressMeter = "1.10" Reexport = "1.2" -Symbolics ="≥5.17" +Symbolics = "≥5.17" +TestItemRunner = "0.2" TestItems = "0.1" TrajectoryIndexingUtils = "0.1" julia = "1.10" diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 999ed043..9115317e 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -5,9 +5,17 @@ manifest_format = "2.0" project_hash = "46fa82f4324d65b2cf462382aecded8067fca5f1" [[deps.ADTypes]] -git-tree-sha1 = "41c37aa88889c171f1300ceac1313c06e891d245" +git-tree-sha1 = "99a6f5d0ce1c7c6afdb759daa30226f71c54f6b0" uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" -version = "0.2.6" +version = "1.7.1" + + [deps.ADTypes.extensions] + ADTypesChainRulesCoreExt = "ChainRulesCore" + ADTypesEnzymeCoreExt = "EnzymeCore" + + [deps.ADTypes.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" [[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" @@ -54,26 +62,53 @@ weakdeps = ["ChainRulesCore", "Test"] AbstractFFTsChainRulesCoreExt = "ChainRulesCore" AbstractFFTsTestExt = "Test" -[[deps.AbstractLattices]] -git-tree-sha1 = "222ee9e50b98f51b5d78feb93dd928880df35f06" -uuid = "398f06c4-4d28-53ec-89ca-5b2656b7603d" -version = "0.3.0" - [[deps.AbstractTrees]] git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" version = "0.4.5" +[[deps.Accessors]] +deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown", "Test"] +git-tree-sha1 = "f61b15be1d76846c0ce31d3fcfac5380ae53db6a" +uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" +version = "0.1.37" + + [deps.Accessors.extensions] + AccessorsAxisKeysExt = "AxisKeys" + AccessorsIntervalSetsExt = "IntervalSets" + AccessorsStaticArraysExt = "StaticArrays" + AccessorsStructArraysExt = "StructArrays" + AccessorsUnitfulExt = "Unitful" + + [deps.Accessors.weakdeps] + AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + Requires = "ae029012-a4dd-5104-9daa-d747884805df" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + [[deps.Adapt]] deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "0fb305e0253fd4e833d486914367a2ee2c2e78d0" +git-tree-sha1 = "6a55b747d1812e699320963ffde36f1ebdda4099" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "4.0.1" +version = "4.0.4" weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" +[[deps.AdaptivePredicates]] +git-tree-sha1 = "7d5da5dd472490d048b081ca1bda4a7821b06456" +uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" +version = "1.1.1" + +[[deps.AliasTables]] +deps = ["PtrArrays", "Random"] +git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" +uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" +version = "1.1.3" + [[deps.Animations]] deps = ["Colors"] git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d" @@ -85,16 +120,20 @@ uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" [[deps.ArrayInterface]] -deps = ["Adapt", "LinearAlgebra", "Requires", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "c5aeb516a84459e0318a02507d2261edad97eb75" +deps = ["Adapt", "LinearAlgebra"] +git-tree-sha1 = "f54c23a5d304fb87110de62bace7777d59088c34" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.7.1" +version = "7.15.0" [deps.ArrayInterface.extensions] ArrayInterfaceBandedMatricesExt = "BandedMatrices" ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" ArrayInterfaceCUDAExt = "CUDA" + ArrayInterfaceCUDSSExt = "CUDSS" + ArrayInterfaceChainRulesExt = "ChainRules" ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" + ArrayInterfaceReverseDiffExt = "ReverseDiff" + ArrayInterfaceSparseArraysExt = "SparseArrays" ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" ArrayInterfaceTrackerExt = "Tracker" @@ -102,7 +141,11 @@ version = "7.7.1" BandedMatrices = "aae01518-5342-5314-be14-df237901396f" BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" + ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" @@ -111,9 +154,9 @@ uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [[deps.Automa]] deps = ["PrecompileTools", "TranscodingStreams"] -git-tree-sha1 = "588e0d680ad1d7201d4c6a804dcb1cd9cba79fbb" +git-tree-sha1 = "014bc22d6c400a7703c0f5dc1fdc302440cf88be" uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" -version = "1.0.3" +version = "1.0.4" [[deps.AxisAlgorithms]] deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] @@ -132,19 +175,19 @@ uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[deps.BenchmarkTools]] deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] -git-tree-sha1 = "f1f03a9fa24271160ed7e73051fba3c1a759b53f" +git-tree-sha1 = "f1dff6729bc61f4d49e140da1af55dcd1ac97b2f" uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "1.4.0" +version = "1.5.0" [[deps.Bijections]] -git-tree-sha1 = "c9b163bd832e023571e86d0b90d9de92a9879088" +git-tree-sha1 = "95f5c7e2d177b7ba1a240b0518038b975d72a8c0" uuid = "e2ed5e7c-b2de-5872-ae92-c73ca462fb04" -version = "0.1.6" +version = "0.1.7" [[deps.BitFlags]] -git-tree-sha1 = "2dc09997850d68179b69dafb58ae806167a32b1b" +git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" -version = "0.1.8" +version = "0.1.9" [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -168,33 +211,27 @@ version = "1.0.1+0" [[deps.Cairo]] deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] -git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b" +git-tree-sha1 = "7b6ad8c35f4bc3bca8eb78127c8b99719506a5fb" uuid = "159f3aea-2a34-519c-b102-8c37f9878175" -version = "1.0.5" +version = "1.1.0" [[deps.CairoMakie]] -deps = ["CRC32c", "Cairo", "Colors", "FFTW", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] -git-tree-sha1 = "6dc1bbdd6a133adf4aa751d12dbc2c6ae59f873d" +deps = ["CRC32c", "Cairo", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] +git-tree-sha1 = "d69c7593fe9d7d617973adcbe4762028c6899b2c" uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" -version = "0.11.9" +version = "0.11.11" [[deps.Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "a2f1c8c668c8e3cb4cca4e57a8efdb09067bb3fd" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.16.1+1" - -[[deps.Calculus]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" -uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" -version = "0.5.1" +version = "1.18.0+2" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "aef70bb349b20aa81a82a19704c3ef339d4ee494" +git-tree-sha1 = "71acdbf594aab5bbb2cec89b208c41b4c411e49f" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.22.1" +version = "1.24.0" weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] @@ -202,21 +239,21 @@ weakdeps = ["SparseArrays"] [[deps.CodeTracking]] deps = ["InteractiveUtils", "UUIDs"] -git-tree-sha1 = "c0216e792f518b39b22212127d4a84dc31e4e386" +git-tree-sha1 = "7eee164f122511d3e4e1ebadb7956939ea7e1c77" uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" -version = "1.3.5" +version = "1.3.6" [[deps.CodecBzip2]] -deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"] -git-tree-sha1 = "9b1ca1aa6ce3f71b3d1840c538a8210a043625eb" +deps = ["Bzip2_jll", "TranscodingStreams"] +git-tree-sha1 = "e7c529cc31bb85b97631b922fa2e6baf246f5905" uuid = "523fee87-0ab8-5b00-afb7-3ecf72e48cfd" -version = "0.8.2" +version = "0.8.4" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73" +git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.4" +version = "0.7.6" [[deps.ColorBrewer]] deps = ["Colors", "JSON", "Test"] @@ -226,15 +263,15 @@ version = "0.4.0" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129" +git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.24.0" +version = "3.26.0" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" +git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.4" +version = "0.11.5" [[deps.ColorVectorSpace]] deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] @@ -248,9 +285,9 @@ weakdeps = ["SpecialFunctions"] [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a" +git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.10" +version = "0.12.11" [[deps.Combinatorics]] git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" @@ -268,11 +305,16 @@ git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7" uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" version = "0.3.0" +[[deps.CommonWorldInvalidations]] +git-tree-sha1 = "ae52d1c52048455e85a387fbee9be553ec2b68d0" +uuid = "f70d9fcc-98c5-4d4a-abd7-e4cdeebd8ca8" +version = "1.0.0" + [[deps.Compat]] deps = ["TOML", "UUIDs"] -git-tree-sha1 = "d2c021fbdde94f6cdaa799639adfeeaa17fd67f5" +git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.13.0" +version = "4.16.0" weakdeps = ["Dates", "LinearAlgebra"] [deps.Compat.extensions] @@ -284,27 +326,36 @@ uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" version = "1.1.1+0" [[deps.CompositeTypes]] -git-tree-sha1 = "02d2316b7ffceff992f3096ae48c7829a8aa0638" +git-tree-sha1 = "bce26c3dab336582805503bed209faab1c279768" uuid = "b152e2b5-7a66-4b01-a709-34e65c35f657" -version = "0.1.3" +version = "0.1.4" + +[[deps.CompositionsBase]] +git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad" +uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b" +version = "0.1.2" +weakdeps = ["InverseFunctions"] + + [deps.CompositionsBase.extensions] + CompositionsBaseInverseFunctionsExt = "InverseFunctions" [[deps.ConcurrentUtilities]] deps = ["Serialization", "Sockets"] -git-tree-sha1 = "9c4708e3ed2b799e6124b5673a712dda0b596a9b" +git-tree-sha1 = "ea32b83ca4fefa1768dc84e504cc0a94fb1ab8d1" uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.3.1" +version = "2.4.2" [[deps.Conda]] deps = ["Downloads", "JSON", "VersionParsing"] -git-tree-sha1 = "51cab8e982c5b598eea9c8ceaced4b58d9dd37c9" +git-tree-sha1 = "b19db3927f0db4151cb86d073689f2428e524576" uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d" -version = "1.10.0" +version = "1.10.2" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] -git-tree-sha1 = "c53fc348ca4d40d7b371e71fd52251839080cbc9" +git-tree-sha1 = "a33b7ced222c6165f624a3f2b55945fac5a598d9" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.4" +version = "1.5.7" weakdeps = ["IntervalSets", "StaticArrays"] [deps.ConstructionBase.extensions] @@ -312,9 +363,9 @@ weakdeps = ["IntervalSets", "StaticArrays"] ConstructionBaseStaticArraysExt = "StaticArrays" [[deps.Contour]] -git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" +git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.6.2" +version = "0.6.3" [[deps.DataAPI]] git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" @@ -323,9 +374,9 @@ version = "1.16.0" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "1fb174f0d48fe7d142e1109a10636bc1d14f5ac2" +git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.17" +version = "0.18.20" [[deps.DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" @@ -337,10 +388,10 @@ deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" [[deps.DelaunayTriangulation]] -deps = ["DataStructures", "EnumX", "ExactPredicates", "Random", "SimpleGraphs"] -git-tree-sha1 = "d4e9dc4c6106b8d44e40cd4faf8261a678552c7c" +deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"] +git-tree-sha1 = "b5f1c6532d2ea71e99b74231b0a3d53fba846ced" uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" -version = "0.8.12" +version = "1.1.3" [[deps.DiffResults]] deps = ["StaticArraysCore"] @@ -359,10 +410,10 @@ deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[deps.Distributions]] -deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "7c302d7a5fec5214eb8a5a4c466dcf7a51fcf169" +deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] +git-tree-sha1 = "0e0a1264b0942f1f3abb2b30891f2a590cc652ac" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.107" +version = "0.25.110" [deps.Distributions.extensions] DistributionsChainRulesCoreExt = "ChainRulesCore" @@ -381,33 +432,31 @@ uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" version = "0.9.3" [[deps.Documenter]] -deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "Test", "Unicode"] -git-tree-sha1 = "2613dbec8f4748273bbe30ba71fd5cb369966bac" +deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] +git-tree-sha1 = "76deb8c15f37a3853f13ea2226b8f2577652de05" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.2.1" +version = "1.5.0" [[deps.DomainSets]] deps = ["CompositeTypes", "IntervalSets", "LinearAlgebra", "Random", "StaticArrays"] -git-tree-sha1 = "f30fd5d8e898141f19cc564f00072bb8e2d97d7d" +git-tree-sha1 = "490392af2c7d63183bfa2c8aaa6ab981c5ba7561" uuid = "5b8099bc-c8ec-5219-889f-1d9e522a28bf" -version = "0.7.5" +version = "0.7.14" +weakdeps = ["Makie"] + + [deps.DomainSets.extensions] + DomainSetsMakieExt = "Makie" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" -[[deps.DualNumbers]] -deps = ["Calculus", "NaNMath", "SpecialFunctions"] -git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566" -uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" -version = "0.6.8" - [[deps.DynamicPolynomials]] deps = ["Future", "LinearAlgebra", "MultivariatePolynomials", "MutableArithmetics", "Pkg", "Reexport", "Test"] -git-tree-sha1 = "0bb0a6f812213ecc8fbbcf472f4a993036858971" +git-tree-sha1 = "30a1848c4f4fc35d1d4bbbd125650f6a11b5bc6c" uuid = "7c1d4256-1411-5781-91ec-d7bc3513ac07" -version = "0.5.5" +version = "0.5.7" [[deps.EarCut_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -440,25 +489,31 @@ version = "0.1.10" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" +git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.5.0+0" +version = "2.6.2+0" [[deps.ExponentialAction]] deps = ["AbstractDifferentiation", "ChainRulesCore", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "10450af8987f4fc4c6ac4f22d0b35146730ad63d" +git-tree-sha1 = "f09115e954f495cab57771b30a31b06b04e79d31" uuid = "e24c0720-ea99-47e8-929e-571b494574d3" -version = "0.2.8" +version = "0.2.9" [[deps.ExprTools]] git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" version = "0.1.10" +[[deps.Expronicon]] +deps = ["MLStyle", "Pkg", "TOML"] +git-tree-sha1 = "fc3951d4d398b5515f91d7fe5d45fc31dccb3c9b" +uuid = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636" +version = "0.8.5" + [[deps.Extents]] -git-tree-sha1 = "2140cd04483da90b2da7f99b2add0750504fc39c" +git-tree-sha1 = "94997910aca72897524d2237c41eb852153b0f65" uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" -version = "0.1.2" +version = "0.1.3" [[deps.FFMPEG_jll]] deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] @@ -480,9 +535,9 @@ version = "3.3.10+0" [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "c5c28c245101bd59154f649e19b038d15901b5dc" +git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.2" +version = "1.16.3" [[deps.FilePaths]] deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"] @@ -500,10 +555,10 @@ version = "0.9.21" uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[deps.FillArrays]] -deps = ["LinearAlgebra", "Random"] -git-tree-sha1 = "5b93957f6dcd33fc343044af3d48c215be2562f1" +deps = ["LinearAlgebra"] +git-tree-sha1 = "fd0002c0b5362d7eb952450ad5eb742443340d6e" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.9.3" +version = "1.12.0" weakdeps = ["PDMats", "SparseArrays", "Statistics"] [deps.FillArrays.extensions] @@ -511,44 +566,22 @@ weakdeps = ["PDMats", "SparseArrays", "Statistics"] FillArraysSparseArraysExt = "SparseArrays" FillArraysStatisticsExt = "Statistics" -[[deps.FiniteDiff]] -deps = ["ArrayInterface", "LinearAlgebra", "Requires", "Setfield", "SparseArrays"] -git-tree-sha1 = "73d1214fec245096717847c62d389a5d2ac86504" -uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.22.0" - - [deps.FiniteDiff.extensions] - FiniteDiffBandedMatricesExt = "BandedMatrices" - FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" - FiniteDiffStaticArraysExt = "StaticArrays" - - [deps.FiniteDiff.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - [[deps.FixedPointNumbers]] deps = ["Statistics"] -git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.4" +version = "0.8.5" [[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] +git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.93+0" +version = "2.13.96+0" [[deps.Format]] -git-tree-sha1 = "f3cf88025f6d03c194d73f5d13fee9004a108329" +git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" -version = "1.3.6" - -[[deps.Formatting]] -deps = ["Printf"] -git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" -uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" -version = "0.4.2" +version = "1.3.7" [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] @@ -568,21 +601,21 @@ version = "4.1.1" [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" +git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.1+0" +version = "2.13.2+0" [[deps.FreeTypeAbstraction]] deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] -git-tree-sha1 = "055626e1a35f6771fe99060e835b72ca61a52621" +git-tree-sha1 = "2493cdfd0740015955a8e46de4ef28f49460d8bc" uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" -version = "0.10.1" +version = "0.10.3" [[deps.FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.10+0" +version = "1.0.14+0" [[deps.FunctionWrappers]] git-tree-sha1 = "d62485945ce5ae9c0c48f124a84998d755bae00e" @@ -607,15 +640,15 @@ version = "0.1.6" [[deps.GeoInterface]] deps = ["Extents"] -git-tree-sha1 = "d4f85701f569584f2cff7ba67a137d03f0cfb7d0" +git-tree-sha1 = "9fff8990361d5127b770e3454488360443019bb3" uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" -version = "1.3.3" +version = "1.3.5" [[deps.GeometryBasics]] deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "5694b56ccf9d15addedc35e9a4ba9c317721b788" +git-tree-sha1 = "b62f2b2d76cee0d61a2ef2b3118cd2a3215d3134" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.4.10" +version = "0.4.11" [[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] @@ -625,21 +658,21 @@ version = "0.21.0+0" [[deps.Git]] deps = ["Git_jll"] -git-tree-sha1 = "51764e6c2e84c37055e846c516e9015b4a291c7d" +git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" -version = "1.3.0" +version = "1.3.1" [[deps.Git_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "b30c473c97fcc1e1e44fab8f3e88fd1b89c9e9d1" +git-tree-sha1 = "d18fb8a1f3609361ebda9bf029b60fd0f120c809" uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.43.0+0" +version = "2.44.0+2" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "e94c92c7bf4819685eb80186d51c43e71d4afa17" +git-tree-sha1 = "7c82e6a6cd34e9d935e9aa4051b66c6ff3af59ba" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.76.5+0" +version = "2.80.2+0" [[deps.Graphics]] deps = ["Colors", "LinearAlgebra", "NaNMath"] @@ -655,9 +688,9 @@ version = "1.3.14+0" [[deps.GridLayoutBase]] deps = ["GeometryBasics", "InteractiveUtils", "Observables"] -git-tree-sha1 = "af13a277efd8a6e716d79ef635d5342ccb75be61" +git-tree-sha1 = "6f93a83ca11346771a93bbde2bdad2f65b61498f" uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" -version = "0.10.0" +version = "0.10.2" [[deps.Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" @@ -666,9 +699,9 @@ version = "1.0.2" [[deps.HTTP]] deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "ac7b73d562b8f4287c3b67b4c66a5395a19c1ae8" +git-tree-sha1 = "d1d712be3164d61d1fb98e7ce9bcbc6cc06b45ed" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.10.2" +version = "1.10.8" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] @@ -678,27 +711,27 @@ version = "2.8.1+1" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "ca0f6bf568b4bfc807e7537f081c81e35ceca114" +git-tree-sha1 = "5e19e1e4fa3e71b774ce746274364aef0234634e" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.10.0+0" +version = "2.11.1+0" [[deps.HypergeometricFunctions]] -deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "f218fe3736ddf977e0e772bc9a586b2383da2685" +deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] +git-tree-sha1 = "7c4195be1649ae622304031ed46a2f4df989f1eb" uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.23" +version = "0.3.24" [[deps.IJulia]] deps = ["Base64", "Conda", "Dates", "InteractiveUtils", "JSON", "Libdl", "Logging", "Markdown", "MbedTLS", "Pkg", "Printf", "REPL", "Random", "SoftGlobalScope", "Test", "UUIDs", "ZMQ"] -git-tree-sha1 = "47ac8cc196b81001a711f4b2c12c97372338f00c" +git-tree-sha1 = "1702f79fa30f56b68d5b2fd6fb3a9a14ff6f9130" uuid = "7073ff75-c697-5162-941a-fcdaad2a7d2a" -version = "1.24.2" +version = "1.25.0" [[deps.IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "8b72179abc660bfab5e28472e019392b97d0985c" +git-tree-sha1 = "b6d6bfdd7ce25b0f9b2f6b3dd56b2673a66c8770" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.4" +version = "0.2.5" [[deps.IfElse]] git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" @@ -725,9 +758,9 @@ version = "0.10.2" [[deps.ImageIO]] deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs"] -git-tree-sha1 = "bca20b2f5d00c4fbc192c3212da8fa79f4688009" +git-tree-sha1 = "437abb322a41d527c197fa800455f79d414f0a3c" uuid = "82e4d734-157c-48bb-816b-45c225c6df19" -version = "0.6.7" +version = "0.6.8" [[deps.ImageMetadata]] deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] @@ -737,9 +770,9 @@ version = "0.9.9" [[deps.Imath_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3d09a9f60edf77f8a4d99f9e015e8fbf9989605d" +git-tree-sha1 = "0936ba688c6d201805a83da835b55c61a180db52" uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" -version = "3.1.7+0" +version = "3.1.11+0" [[deps.IndirectArrays]] git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" @@ -747,20 +780,15 @@ uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" version = "1.0.0" [[deps.Inflate]] -git-tree-sha1 = "ea8031dea4aff6bd41f1df8f2fdfb25b33626381" +git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.4" - -[[deps.IntegerMathUtils]] -git-tree-sha1 = "b8ffb903da9f7b8cf695a8bead8e01814aa24b30" -uuid = "18e54dd8-cb9d-406c-a71d-865a43cbb235" -version = "0.1.2" +version = "0.1.5" [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5fdf2fe6724d8caabf43b557b84ce53f3b7e2f6b" +git-tree-sha1 = "14eb2b542e748570b56446f4c50fbfb2306ebc45" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.0.2+0" +version = "2024.2.0+0" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -779,10 +807,10 @@ version = "0.15.1" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [[deps.IntervalArithmetic]] -deps = ["CRlibm_jll", "RoundingEmulator"] -git-tree-sha1 = "2d6d22fe481eff6e337808cc0880c567d7324f9a" +deps = ["CRlibm_jll", "MacroTools", "RoundingEmulator"] +git-tree-sha1 = "433b0bb201cd76cb087b017e49244f10394ebe9c" uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" -version = "0.22.8" +version = "0.22.14" weakdeps = ["DiffRules", "ForwardDiff", "RecipesBase"] [deps.IntervalArithmetic.extensions] @@ -801,17 +829,27 @@ weakdeps = ["Random", "RecipesBase", "Statistics"] IntervalSetsRecipesBaseExt = "RecipesBase" IntervalSetsStatisticsExt = "Statistics" +[[deps.InverseFunctions]] +git-tree-sha1 = "2787db24f4e03daf859c6509ff87764e4182f7d1" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.16" +weakdeps = ["Dates", "Test"] + + [deps.InverseFunctions.extensions] + InverseFunctionsDatesExt = "Dates" + InverseFunctionsTestExt = "Test" + [[deps.Ipopt]] deps = ["Ipopt_jll", "LinearAlgebra", "MathOptInterface", "OpenBLAS32_jll", "PrecompileTools"] -git-tree-sha1 = "6600353576cee7e7388e57e94115f6aee034fb1c" +git-tree-sha1 = "92588db78296190d27668a560df3997719fc2a25" uuid = "b6b21f68-93f8-5de0-b562-5493be1d77c9" -version = "1.6.1" +version = "1.6.6" [[deps.Ipopt_jll]] deps = ["ASL_jll", "Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MUMPS_seq_jll", "SPRAL_jll", "libblastrampoline_jll"] -git-tree-sha1 = "546c40fd3718c65d48296dd6cec98af9904e3ca4" +git-tree-sha1 = "a0950d209a055b3adb6d29ade5cbdf005a6bd290" uuid = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7" -version = "300.1400.1400+0" +version = "300.1400.1600+0" [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" @@ -835,10 +873,10 @@ uuid = "82899510-4779-5014-852e-03e436cf321d" version = "1.0.0" [[deps.JLD2]] -deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "Pkg", "PrecompileTools", "Printf", "Reexport", "Requires", "TranscodingStreams", "UUIDs"] -git-tree-sha1 = "5ea6acdd53a51d897672edb694e3cc2912f3f8a7" +deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "PrecompileTools", "Reexport", "Requires", "TranscodingStreams", "UUIDs", "Unicode"] +git-tree-sha1 = "67d4690d32c22e28818a434b293a374cc78473d3" uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" -version = "0.4.46" +version = "0.4.51" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] @@ -860,39 +898,39 @@ version = "0.1.5" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3336abae9a713d2210bb57ab484b1e065edd7d23" +git-tree-sha1 = "c84a835e1a09b289ffcd2271bf2a337bbdda6637" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.2+0" +version = "3.0.3+0" [[deps.JuliaInterpreter]] deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] -git-tree-sha1 = "7b762d81887160169ddfc93a47e5fd7a6a3e78ef" +git-tree-sha1 = "7ae67d8567853d367e3463719356b8989e236069" uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" -version = "0.9.29" +version = "0.9.34" [[deps.KernelDensity]] deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] -git-tree-sha1 = "fee018a29b60733876eb557804b5b109dd3dd8a7" +git-tree-sha1 = "7d703202e65efa1369de1279c162b915e245eed1" uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" -version = "0.6.8" +version = "0.6.9" [[deps.LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.1+0" +version = "3.100.2+0" [[deps.LLVMOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "d986ce2d884d49126836ea94ed5bfb0f12679713" +git-tree-sha1 = "e16271d212accd09d52ee0ae98956b8a05c4b626" uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "15.0.7+0" +version = "17.0.6+0" [[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "70c5da094887fd2cae843b8db33920bac4b6f07d" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.1+0" +version = "2.10.2+0" [[deps.LaTeXStrings]] git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" @@ -901,9 +939,9 @@ version = "1.3.1" [[deps.LabelledArrays]] deps = ["ArrayInterface", "ChainRulesCore", "ForwardDiff", "LinearAlgebra", "MacroTools", "PreallocationTools", "RecursiveArrayTools", "StaticArrays"] -git-tree-sha1 = "d1f981fba6eb3ec393eede4821bca3f2b7592cd4" +git-tree-sha1 = "e459fda6b68ea8684b3fcd513d2fd1e5130c4402" uuid = "2ee39098-c373-598a-b85f-a56591580800" -version = "1.15.1" +version = "1.16.0" [[deps.LambertW]] git-tree-sha1 = "c5ffc834de5d61d00d2b0e18c96267cffc21f648" @@ -911,17 +949,19 @@ uuid = "984bce1d-4616-540c-a9ee-88d1112d94c9" version = "0.4.6" [[deps.Latexify]] -deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Printf", "Requires"] -git-tree-sha1 = "f428ae552340899a935973270b8d98e5a31c49fe" +deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] +git-tree-sha1 = "ce5f5621cac23a86011836badfedf664a612cee4" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.1" +version = "0.16.5" [deps.Latexify.extensions] DataFramesExt = "DataFrames" + SparseArraysExt = "SparseArrays" SymEngineExt = "SymEngine" [deps.Latexify.weakdeps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" [[deps.LazilyInitializedFields]] @@ -929,12 +969,6 @@ git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" version = "1.2.2" -[[deps.Lazy]] -deps = ["MacroTools"] -git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f" -uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" -version = "0.15.1" - [[deps.LazyArtifacts]] deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" @@ -978,16 +1012,16 @@ uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" version = "3.2.2+1" [[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] +git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.7+0" +version = "1.8.11+0" [[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.42.0+0" +version = "1.49.0+0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -996,56 +1030,38 @@ uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" version = "1.17.0+0" [[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.35.0+0" +version = "2.40.1+0" [[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.36.0+0" - -[[deps.LightXML]] -deps = ["Libdl", "XML2_jll"] -git-tree-sha1 = "3a994404d3f6709610701c7dabfc03fed87a81f8" -uuid = "9c8b4983-aa76-5018-a973-4c85ecc9e179" -version = "0.9.1" - -[[deps.LineSearches]] -deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "Printf"] -git-tree-sha1 = "7bbea35cec17305fc70a0e5b4641477dc0789d9d" -uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" -version = "7.2.0" +version = "2.40.1+0" [[deps.LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -[[deps.LinearAlgebraX]] -deps = ["LinearAlgebra", "Mods", "Primes", "SimplePolynomials"] -git-tree-sha1 = "d76cec8007ec123c2b681269d40f94b053473fcf" -uuid = "9b3f67b0-2d00-526e-9884-9e4938f8fb88" -version = "0.2.7" - [[deps.Literate]] deps = ["Base64", "IOCapture", "JSON", "REPL"] -git-tree-sha1 = "bad26f1ccd99c553886ec0725e99a509589dcd11" +git-tree-sha1 = "eef2e1fc1dc38af90a18eb16e519e06d1fd10c2a" uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306" -version = "2.16.1" +version = "2.19.0" [[deps.LiveServer]] deps = ["HTTP", "LoggingExtras", "MIMEs", "Pkg", "Sockets", "Test"] -git-tree-sha1 = "24d05efe53436b22a42bf2ae459f47c48b0c2603" +git-tree-sha1 = "1e46b873b8ef176e23ee43f96e72cd45c20bafb4" uuid = "16fef848-5104-11e9-1b77-fb7a48bbb589" -version = "1.2.7" +version = "1.3.1" [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" +git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.27" +version = "0.3.28" [deps.LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -1068,9 +1084,9 @@ version = "1.0.3" [[deps.LoweredCodeUtils]] deps = ["JuliaInterpreter"] -git-tree-sha1 = "31e27f0b0bf0df3e3e951bfcc43fe8c730a219f6" +git-tree-sha1 = "1ce1834f9644a8f7c011eb0592b7fd6c42c90653" uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" -version = "2.4.5" +version = "3.0.1" [[deps.METIS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -1084,16 +1100,21 @@ uuid = "6c6e2e6c-3030-632d-7369-2d6c69616d65" version = "0.1.4" [[deps.MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "72dc3cf284559eb8f53aa593fe62cb33f83ed0c0" +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] +git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.0.0+0" +version = "2024.2.0+0" + +[[deps.MLStyle]] +git-tree-sha1 = "bc38dff0548128765760c79eb7388a4b37fae2c8" +uuid = "d8e11817-5142-5d16-987a-aa16d5891078" +version = "0.4.17" [[deps.MUMPS_seq_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "840b83c65b27e308095c139a457373850b2f5977" +git-tree-sha1 = "85047ac569761e3387717480a38a61d2a67df45c" uuid = "d7ed1dd3-d0ae-5e8e-bfb4-87a502085b8d" -version = "500.600.201+0" +version = "500.700.300+0" [[deps.MacroTools]] deps = ["Markdown", "Random"] @@ -1102,10 +1123,10 @@ uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.13" [[deps.Makie]] -deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "InteractiveUtils", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "StableHashTraits", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun"] -git-tree-sha1 = "27af6be179c711fb916a597b6644fbb5b80becc0" +deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "InteractiveUtils", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun"] +git-tree-sha1 = "4d49c9ee830eec99d3e8de2425ff433ece7cc1bc" uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" -version = "0.20.8" +version = "0.20.10" [[deps.MakieCore]] deps = ["Observables", "REPL"] @@ -1130,9 +1151,9 @@ version = "0.1.2" [[deps.MathOptInterface]] deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MutableArithmetics", "NaNMath", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays", "SpecialFunctions", "Test", "Unicode"] -git-tree-sha1 = "e8b98c868029d007102dc5f98986c81f33b0ec37" +git-tree-sha1 = "c0fe113e9c72aa0c9a185fd3c5ca1daa51de1486" uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" -version = "1.26.0" +version = "1.31.1" [[deps.MathTeXEngine]] deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] @@ -1153,18 +1174,13 @@ version = "2.28.2+1" [[deps.Missings]] deps = ["DataAPI"] -git-tree-sha1 = "f66bdc5de519e8f8ae43bdc598782d35a25b1272" +git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.1.0" +version = "1.2.0" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" -[[deps.Mods]] -git-tree-sha1 = "924f962b524a71eef7a21dae1e6853817f9b658f" -uuid = "7475f97c-0381-53b1-977b-4c60186c8d62" -version = "2.2.4" - [[deps.MosaicViews]] deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe" @@ -1175,28 +1191,17 @@ version = "0.3.4" uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2023.1.10" -[[deps.Multisets]] -git-tree-sha1 = "8d852646862c96e226367ad10c8af56099b4047e" -uuid = "3b2b4ff1-bcff-5658-a3ee-dbcf1ce5ac09" -version = "0.4.4" - [[deps.MultivariatePolynomials]] deps = ["ChainRulesCore", "DataStructures", "LinearAlgebra", "MutableArithmetics"] -git-tree-sha1 = "769c9175942d91ed9b83fa929eee4fe6a1d128ad" +git-tree-sha1 = "5c1d1d9361e1417e5a065e1f84dc3686cbdaea21" uuid = "102ac46a-7ee4-5c85-9060-abc95bfdeaa3" -version = "0.5.4" +version = "0.5.6" [[deps.MutableArithmetics]] deps = ["LinearAlgebra", "SparseArrays", "Test"] -git-tree-sha1 = "302fd161eb1c439e4115b51ae456da4e9984f130" +git-tree-sha1 = "d0a6b1096b584a2b88efb70a92f8cb8c881eb38a" uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" -version = "1.4.1" - -[[deps.NLSolversBase]] -deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] -git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" -uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" -version = "7.8.3" +version = "1.4.6" [[deps.NaNMath]] deps = ["OpenLibm_jll"] @@ -1205,10 +1210,10 @@ uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" version = "1.0.2" [[deps.NamedTrajectories]] -deps = ["CairoMakie", "JLD2", "LaTeXStrings", "Latexify", "OrderedCollections", "Random", "Reexport", "Revise", "Unidecode"] -git-tree-sha1 = "a109d8dee4055adb47bb435fd479dd67a50776af" +deps = ["CairoMakie", "JLD2", "LaTeXStrings", "Latexify", "OrderedCollections", "Random", "Reexport", "Unidecode"] +git-tree-sha1 = "94fa6864fcf891e7214a1dd73c773d325556de28" uuid = "538bc3a1-5ab9-4fc3-b776-35ca1e893e08" -version = "0.1.5" +version = "0.2.0" [[deps.Netpbm]] deps = ["FileIO", "ImageCore", "ImageMetadata"] @@ -1226,9 +1231,9 @@ uuid = "510215fc-4207-5dde-b226-833fc4488ee2" version = "0.5.5" [[deps.OffsetArrays]] -git-tree-sha1 = "6a731f2b5c03157418a20c12195eb4b74c8f8621" +git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.13.0" +version = "1.14.1" weakdeps = ["Adapt"] [deps.OffsetArrays.extensions] @@ -1259,9 +1264,9 @@ version = "0.3.2" [[deps.OpenEXR_jll]] deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "a4ca623df1ae99d09bc9868b008262d0c0ac1e4f" +git-tree-sha1 = "8292dd5c8a38257111ada2174000a33745b06d4e" uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" -version = "3.1.4+0" +version = "3.2.4+0" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] @@ -1270,15 +1275,15 @@ version = "0.8.1+2" [[deps.OpenSSL]] deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"] -git-tree-sha1 = "51901a49222b09e3743c65b8847687ae5fc78eb2" +git-tree-sha1 = "38cb508d080d21dc1128f7fb04f20387ed4c0af4" uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" -version = "1.4.1" +version = "1.4.3" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "60e3045590bd104a16fefb12836c00c0ef8c7f8c" +git-tree-sha1 = "a028ee3cb5641cccc4c24e90c36b0a4f7707bdf5" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.13+0" +version = "3.0.14+0" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -1286,12 +1291,6 @@ git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" -[[deps.Optim]] -deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", "MathOptInterface", "NLSolversBase", "NaNMath", "Parameters", "PositiveFactorizations", "Printf", "SparseArrays", "StatsBase"] -git-tree-sha1 = "d024bfb56144d947d4fafcd9cb5cafbe3410b133" -uuid = "429524aa-4258-5aef-a3af-852621145aeb" -version = "1.9.2" - [[deps.Opus_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" @@ -1334,15 +1333,9 @@ version = "0.5.12" [[deps.Pango_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4745216e94f71cb768d58330b059c9b76f32cb66" +git-tree-sha1 = "cb5a2ab6763464ae0f19c86c56c63d4a2b0f5bda" uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.50.14+0" - -[[deps.Parameters]] -deps = ["OrderedCollections", "UnPack"] -git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.3" +version = "1.52.2+0" [[deps.Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] @@ -1350,23 +1343,11 @@ git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.8.1" -[[deps.Permutations]] -deps = ["Combinatorics", "LinearAlgebra", "Random"] -git-tree-sha1 = "eb3f9df2457819bf0a9019bd93cc451697a0751e" -uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" -version = "0.4.20" - -[[deps.PikaParser]] -deps = ["DocStringExtensions"] -git-tree-sha1 = "d6ff87de27ff3082131f31a714d25ab6d0a88abf" -uuid = "3bbf5609-3e7b-44cd-8549-7c69f321e792" -version = "0.6.1" - [[deps.Pixman_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87" +git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.42.2+0" +version = "0.43.4+0" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] @@ -1381,39 +1362,20 @@ version = "0.3.3" [[deps.PlotUtils]] deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "862942baf5663da528f66d24996eb6da85218e76" +git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.4.0" +version = "1.4.1" [[deps.PolygonOps]] git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" uuid = "647866c9-e3ac-4575-94e7-e3d426903924" version = "0.1.2" -[[deps.Polynomials]] -deps = ["LinearAlgebra", "RecipesBase", "Setfield", "SparseArrays"] -git-tree-sha1 = "a9c7a523d5ed375be3983db190f6a5874ae9286d" -uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" -version = "4.0.6" -weakdeps = ["ChainRulesCore", "FFTW", "MakieCore", "MutableArithmetics"] - - [deps.Polynomials.extensions] - PolynomialsChainRulesCoreExt = "ChainRulesCore" - PolynomialsFFTWExt = "FFTW" - PolynomialsMakieCoreExt = "MakieCore" - PolynomialsMutableArithmeticsExt = "MutableArithmetics" - -[[deps.PositiveFactorizations]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "17275485f373e6673f7e7f97051f703ed5b15b20" -uuid = "85a6dd25-e78a-55b7-8502-1745935b8125" -version = "0.2.4" - [[deps.PreallocationTools]] deps = ["Adapt", "ArrayInterface", "ForwardDiff"] -git-tree-sha1 = "b6665214f2d0739f2d09a17474dd443b9139784a" +git-tree-sha1 = "6c62ce45f268f3f958821a1e5192cf91c75ae89c" uuid = "d236fae5-4411-538c-8e31-a6e3d9e00b46" -version = "0.4.20" +version = "0.4.24" [deps.PreallocationTools.extensions] PreallocationToolsReverseDiffExt = "ReverseDiff" @@ -1423,21 +1385,15 @@ version = "0.4.20" [[deps.PrecompileTools]] deps = ["Preferences"] -git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f" +git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.0" +version = "1.2.1" [[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" +git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.1" - -[[deps.Primes]] -deps = ["IntegerMathUtils"] -git-tree-sha1 = "cb420f77dc474d23ee47ca8d14c90810cafe69e7" -uuid = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" -version = "0.5.6" +version = "1.4.3" [[deps.Printf]] deps = ["Unicode"] @@ -1449,9 +1405,14 @@ uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" [[deps.ProgressMeter]] deps = ["Distributed", "Printf"] -git-tree-sha1 = "00099623ffee15972c16111bcf84c58a0051257c" +git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4" uuid = "92933f4c-e287-5a05-a399-4b506db050ca" -version = "1.9.0" +version = "1.10.2" + +[[deps.PtrArrays]] +git-tree-sha1 = "f011fbb92c4d401059b2212c05c0601b70f8b759" +uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" +version = "1.2.0" [[deps.QOI]] deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] @@ -1461,12 +1422,12 @@ version = "1.0.0" [[deps.QuadGK]] deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "9b23c31e76e333e6fb4c1595ae6afa74966a729e" +git-tree-sha1 = "e237232771fdafbae3db5c31275303e056afaa9f" uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.9.4" +version = "2.10.1" [[deps.QuantumCollocation]] -deps = ["BenchmarkTools", "CairoMakie", "Distributions", "Einsum", "ExponentialAction", "ForwardDiff", "IJulia", "Ipopt", "JLD2", "Libdl", "LinearAlgebra", "MathOptInterface", "NamedTrajectories", "Random", "Reexport", "Revise", "SparseArrays", "Symbolics", "TestItemRunner", "TestItems", "TrajectoryIndexingUtils"] +deps = ["BenchmarkTools", "CairoMakie", "Distributions", "Einsum", "ExponentialAction", "ForwardDiff", "IJulia", "Ipopt", "JLD2", "Libdl", "LinearAlgebra", "MathOptInterface", "NamedTrajectories", "ProgressMeter", "Random", "Reexport", "SparseArrays", "Symbolics", "TestItemRunner", "TestItems", "TrajectoryIndexingUtils"] path = ".." uuid = "0dc23a59-5ffb-49af-b6bd-932a8ae77adf" version = "0.2.0" @@ -1501,10 +1462,10 @@ uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" version = "1.3.4" [[deps.RecursiveArrayTools]] -deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "SparseArrays", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "1bbc4bb050165cc57ca2876cd53cc23395948650" +deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] +git-tree-sha1 = "b034171b93aebc81b3e1890a036d13a9c4a9e3e0" uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" -version = "3.10.0" +version = "3.27.0" [deps.RecursiveArrayTools.extensions] RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" @@ -1512,6 +1473,7 @@ version = "3.10.0" RecursiveArrayToolsMeasurementsExt = "Measurements" RecursiveArrayToolsMonteCarloMeasurementsExt = "MonteCarloMeasurements" RecursiveArrayToolsReverseDiffExt = ["ReverseDiff", "Zygote"] + RecursiveArrayToolsSparseArraysExt = ["SparseArrays"] RecursiveArrayToolsTrackerExt = "Tracker" RecursiveArrayToolsZygoteExt = "Zygote" @@ -1521,6 +1483,7 @@ version = "3.10.0" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" @@ -1548,16 +1511,10 @@ uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" [[deps.Revise]] -deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Pkg", "REPL", "Requires", "UUIDs", "Unicode"] -git-tree-sha1 = "12aa2d7593df490c407a3bbd8b86b8b515017f3e" +deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "REPL", "Requires", "UUIDs", "Unicode"] +git-tree-sha1 = "7b7850bb94f75762d567834d7e9802fc22d62f9c" uuid = "295af30f-e4ad-537b-8983-00126c2a3abe" -version = "3.5.14" - -[[deps.RingLists]] -deps = ["Random"] -git-tree-sha1 = "f39da63aa6d2d88e0c1bd20ed6a3ff9ea7171ada" -uuid = "286e9d63-9694-5540-9e3c-4e6708fa07b2" -version = "0.2.8" +version = "3.5.18" [[deps.Rmath]] deps = ["Random", "Rmath_jll"] @@ -1566,10 +1523,10 @@ uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" version = "0.7.1" [[deps.Rmath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6ed52fdd3382cf21947b15e8870ac0ddbff736da" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e60724fd3beea548353984dc61c943ecddb0e29a" uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.4.0+0" +version = "0.4.3+0" [[deps.RoundingEmulator]] git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" @@ -1578,25 +1535,31 @@ version = "0.2.1" [[deps.RuntimeGeneratedFunctions]] deps = ["ExprTools", "SHA", "Serialization"] -git-tree-sha1 = "6aacc5eefe8415f47b3e34214c1d79d2674a0ba2" +git-tree-sha1 = "04c968137612c4a5629fa531334bb81ad5680f00" uuid = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" -version = "0.5.12" +version = "0.5.13" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" +[[deps.SIMD]] +deps = ["PrecompileTools"] +git-tree-sha1 = "2803cab51702db743f3fda07dd1745aadfbf43bd" +uuid = "fdea26ae-647d-5447-a871-4b548cad5224" +version = "3.5.0" + [[deps.SPRAL_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "34b9dacd687cace8aa4d550e3e9bb8615f1a61e9" +git-tree-sha1 = "11f3da4b25efacd1cec8e263421f2a9003a5e8e0" uuid = "319450e9-13b8-58e8-aa9f-8fd1420848ab" -version = "2024.1.18+0" +version = "2024.5.8+0" [[deps.SciMLBase]] -deps = ["ADTypes", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FillArrays", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables", "TruncatedStacktraces"] -git-tree-sha1 = "16dd1ea058e1c080d7f1ba47a9094f87a1c50e4c" +deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "Expronicon", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] +git-tree-sha1 = "5123ca064567e81c31fb3acdf15d2c9459bb7cc3" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.26.2" +version = "2.50.0" [deps.SciMLBase.extensions] SciMLBaseChainRulesCoreExt = "ChainRulesCore" @@ -1618,10 +1581,17 @@ version = "2.26.2" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SciMLOperators]] -deps = ["ArrayInterface", "DocStringExtensions", "Lazy", "LinearAlgebra", "Setfield", "SparseArrays", "StaticArraysCore", "Tricks"] -git-tree-sha1 = "51ae235ff058a64815e0a2c34b1db7578a06813d" +deps = ["ArrayInterface", "DocStringExtensions", "LinearAlgebra", "MacroTools", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "23b02c588ac9a17ecb276cc62ab37f3e4fe37b32" uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" -version = "0.3.7" +version = "0.3.9" +weakdeps = ["SparseArrays"] + +[[deps.SciMLStructures]] +deps = ["ArrayInterface"] +git-tree-sha1 = "20ad3e7c137156c50c93c888d0f2bc5b7883c729" +uuid = "53ae85a6-f571-4167-b2af-e1d143709226" +version = "1.4.2" [[deps.Scratch]] deps = ["Dates"] @@ -1665,30 +1635,6 @@ git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1" uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" version = "1.1.0" -[[deps.SimpleGraphs]] -deps = ["AbstractLattices", "Combinatorics", "DataStructures", "IterTools", "LightXML", "LinearAlgebra", "LinearAlgebraX", "Optim", "Primes", "Random", "RingLists", "SimplePartitions", "SimplePolynomials", "SimpleRandom", "SparseArrays", "Statistics"] -git-tree-sha1 = "f65caa24a622f985cc341de81d3f9744435d0d0f" -uuid = "55797a34-41de-5266-9ec1-32ac4eb504d3" -version = "0.8.6" - -[[deps.SimplePartitions]] -deps = ["AbstractLattices", "DataStructures", "Permutations"] -git-tree-sha1 = "e182b9e5afb194142d4668536345a365ea19363a" -uuid = "ec83eff0-a5b5-5643-ae32-5cbf6eedec9d" -version = "0.3.2" - -[[deps.SimplePolynomials]] -deps = ["Mods", "Multisets", "Polynomials", "Primes"] -git-tree-sha1 = "7063828369cafa93f3187b3d0159f05582011405" -uuid = "cc47b68c-3164-5771-a705-2bc0097375a0" -version = "0.2.17" - -[[deps.SimpleRandom]] -deps = ["Distributions", "LinearAlgebra", "Random"] -git-tree-sha1 = "3a6fb395e37afab81aeea85bae48a4db5cd7244a" -uuid = "a6525b86-64cd-54fa-8f65-62fc48bdc0e8" -version = "0.3.1" - [[deps.SimpleTraits]] deps = ["InteractiveUtils", "MacroTools"] git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" @@ -1723,20 +1669,14 @@ version = "1.10.0" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" +git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.3.1" +version = "2.4.0" weakdeps = ["ChainRulesCore"] [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" -[[deps.StableHashTraits]] -deps = ["Compat", "PikaParser", "SHA", "Tables", "TupleTools"] -git-tree-sha1 = "10dc702932fe05a0e09b8e5955f00794ea1e8b12" -uuid = "c5dd0088-6c3f-4803-b00e-f31a60c170fa" -version = "1.1.8" - [[deps.StackViews]] deps = ["OffsetArrays"] git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" @@ -1745,9 +1685,9 @@ version = "0.1.1" [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "bf074c045d3d5ffd956fa0a461da38a44685d6b2" +git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.3" +version = "1.9.7" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -1755,9 +1695,9 @@ weakdeps = ["ChainRulesCore", "Statistics"] StaticArraysStatisticsExt = "Statistics" [[deps.StaticArraysCore]] -git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.2" +version = "1.4.3" [[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] @@ -1772,24 +1712,21 @@ version = "1.7.0" [[deps.StatsBase]] deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "1d77abd07f617c4868c33d4f5b9e1dbb2643c9cf" +git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.2" +version = "0.34.3" [[deps.StatsFuns]] deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] git-tree-sha1 = "cef0472124fab0695b58ca35a77c6fb942fdab8a" uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" version = "1.3.1" +weakdeps = ["ChainRulesCore", "InverseFunctions"] [deps.StatsFuns.extensions] StatsFunsChainRulesCoreExt = "ChainRulesCore" StatsFunsInverseFunctionsExt = "InverseFunctions" - [deps.StatsFuns.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - [[deps.StructArrays]] deps = ["ConstructionBase", "DataAPI", "Tables"] git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be" @@ -1813,29 +1750,40 @@ uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" version = "7.2.1+1" [[deps.SymbolicIndexingInterface]] -git-tree-sha1 = "b74cb9508b6c0aa91d729dcbc7e35faf8998c549" +deps = ["Accessors", "ArrayInterface", "RuntimeGeneratedFunctions", "StaticArraysCore"] +git-tree-sha1 = "c9fce29fb41a10677e24f74421ebe31220b81ad0" uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" -version = "0.3.7" +version = "0.3.28" + +[[deps.SymbolicLimits]] +deps = ["SymbolicUtils"] +git-tree-sha1 = "fabf4650afe966a2ba646cabd924c3fd43577fc3" +uuid = "19f23fe9-fdab-4a78-91af-e7b7767979c3" +version = "0.2.2" [[deps.SymbolicUtils]] -deps = ["AbstractTrees", "Bijections", "ChainRulesCore", "Combinatorics", "ConstructionBase", "DataStructures", "DocStringExtensions", "DynamicPolynomials", "IfElse", "LabelledArrays", "LinearAlgebra", "MultivariatePolynomials", "NaNMath", "Setfield", "SparseArrays", "SpecialFunctions", "StaticArrays", "SymbolicIndexingInterface", "TimerOutputs", "Unityper"] -git-tree-sha1 = "849b1dfb1680a9e9f2c6023f79a49b694fb6d0da" +deps = ["AbstractTrees", "Bijections", "ChainRulesCore", "Combinatorics", "ConstructionBase", "DataStructures", "DocStringExtensions", "DynamicPolynomials", "IfElse", "LabelledArrays", "LinearAlgebra", "MultivariatePolynomials", "NaNMath", "Setfield", "SparseArrays", "SpecialFunctions", "StaticArrays", "SymbolicIndexingInterface", "TermInterface", "TimerOutputs", "Unityper"] +git-tree-sha1 = "d00729521f49d96afd3fcddb437141eb71222886" uuid = "d1185830-fcd6-423d-90d6-eec64667417b" -version = "1.5.0" +version = "3.2.0" [[deps.Symbolics]] -deps = ["ArrayInterface", "Bijections", "ConstructionBase", "DataStructures", "DiffRules", "Distributions", "DocStringExtensions", "DomainSets", "DynamicPolynomials", "ForwardDiff", "IfElse", "LaTeXStrings", "LambertW", "Latexify", "Libdl", "LinearAlgebra", "LogExpFunctions", "MacroTools", "Markdown", "NaNMath", "PrecompileTools", "RecipesBase", "Reexport", "Requires", "RuntimeGeneratedFunctions", "SciMLBase", "Setfield", "SparseArrays", "SpecialFunctions", "StaticArrays", "SymbolicIndexingInterface", "SymbolicUtils"] -git-tree-sha1 = "0cad5780cc4c0f02ca1c27f68231526131c4537e" +deps = ["ADTypes", "ArrayInterface", "Bijections", "CommonWorldInvalidations", "ConstructionBase", "DataStructures", "DiffRules", "Distributions", "DocStringExtensions", "DomainSets", "DynamicPolynomials", "IfElse", "LaTeXStrings", "LambertW", "Latexify", "Libdl", "LinearAlgebra", "LogExpFunctions", "MacroTools", "Markdown", "NaNMath", "PrecompileTools", "RecipesBase", "Reexport", "RuntimeGeneratedFunctions", "SciMLBase", "Setfield", "SparseArrays", "SpecialFunctions", "StaticArraysCore", "SymbolicIndexingInterface", "SymbolicLimits", "SymbolicUtils", "TermInterface"] +git-tree-sha1 = "4224422f6a5452b1accaec15a65a5ce3c2ca4600" uuid = "0c5d862f-8b57-4792-8d23-62f2024744c7" -version = "5.21.0" +version = "6.2.0" [deps.Symbolics.extensions] + SymbolicsForwardDiffExt = "ForwardDiff" SymbolicsGroebnerExt = "Groebner" - SymbolicsPreallocationToolsExt = "PreallocationTools" + SymbolicsLuxCoreExt = "LuxCore" + SymbolicsPreallocationToolsExt = ["PreallocationTools", "ForwardDiff"] SymbolicsSymPyExt = "SymPy" [deps.Symbolics.weakdeps] + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Groebner = "0b43b601-686d-58a3-8a1c-6623616c7cd4" + LuxCore = "bb33d45b-7691-41d6-9220-0943567d0623" PreallocationTools = "d236fae5-4411-538c-8e31-a6e3d9e00b46" SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6" @@ -1851,10 +1799,10 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" [[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.11.1" +version = "1.12.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -1867,6 +1815,11 @@ git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" version = "0.1.1" +[[deps.TermInterface]] +git-tree-sha1 = "d673e0aca9e46a2f63720201f55cc7b3e7169b16" +uuid = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c" +version = "2.0.0" + [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" @@ -1883,16 +1836,16 @@ uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" version = "0.1.1" [[deps.TiffImages]] -deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "UUIDs"] -git-tree-sha1 = "34cc045dd0aaa59b8bbe86c644679bc57f1d5bd0" +deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"] +git-tree-sha1 = "bc7fd5c91041f44636b2c134041f7e5263ce58ae" uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" -version = "0.6.8" +version = "0.10.0" [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] -git-tree-sha1 = "f548a9e9c490030e545f72074a41edfd0e5bcdd7" +git-tree-sha1 = "5a13ae8a41237cff5ecf34f73eb1b8f42fff6531" uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" -version = "0.5.23" +version = "0.5.24" [[deps.TrajectoryIndexingUtils]] git-tree-sha1 = "108da5f045005c9b57e6713ea84aca49d77fccaa" @@ -1900,35 +1853,15 @@ uuid = "6dad8b7f-dd9a-4c28-9b70-85b9a079bfc8" version = "0.1.0" [[deps.TranscodingStreams]] -git-tree-sha1 = "54194d92959d8ebaa8e26227dbe3cdefcdcd594f" +git-tree-sha1 = "e84b3a11b9bece70d14cce63406bbc79ed3464d2" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.3" -weakdeps = ["Random", "Test"] - - [deps.TranscodingStreams.extensions] - TestExt = ["Test", "Random"] - -[[deps.Tricks]] -git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f" -uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" -version = "0.1.8" +version = "0.11.2" [[deps.TriplotBase]] git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" uuid = "981d1d27-644d-49a2-9326-4793e63143c3" version = "0.1.0" -[[deps.TruncatedStacktraces]] -deps = ["InteractiveUtils", "MacroTools", "Preferences"] -git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" -uuid = "781d530d-4396-4725-bb49-402e4bee1e77" -version = "1.4.0" - -[[deps.TupleTools]] -git-tree-sha1 = "41d61b1c545b06279871ef1a4b5fcb2cac2191cd" -uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" -version = "1.5.0" - [[deps.URIs]] git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" @@ -1938,11 +1871,6 @@ version = "1.5.1" deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -[[deps.UnPack]] -git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" -uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" -version = "1.0.2" - [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" @@ -1977,15 +1905,15 @@ version = "1.0.0" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "801cbe47eae69adc50f36c3caec4758d2650741b" +git-tree-sha1 = "d9717ce3518dc68a99e6b96300813760d887a01d" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.12.2+0" +version = "2.13.1+0" [[deps.XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.34+0" +version = "1.1.41+0" [[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] @@ -2006,16 +1934,16 @@ uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" version = "1.1.4+0" [[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.4+4" +version = "1.3.6+0" [[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.10+4" +version = "0.9.11+0" [[deps.Xorg_libpthread_stubs_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -2025,9 +1953,9 @@ version = "0.1.1+0" [[deps.Xorg_libxcb_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "b4bfde5d5b652e22b9c790ad00af08b6d042b97d" +git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.15.0+0" +version = "1.17.0+0" [[deps.Xorg_xtrans_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -2036,16 +1964,16 @@ uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" version = "1.5.0+0" [[deps.ZMQ]] -deps = ["FileWatching", "Sockets", "ZeroMQ_jll"] -git-tree-sha1 = "356d2bdcc0bce90aabee1d1c0f6d6f301eda8f77" +deps = ["FileWatching", "PrecompileTools", "Sockets", "ZeroMQ_jll"] +git-tree-sha1 = "18cfd00df3cbbebf8ea4ec7ea6bbceb3af716bd0" uuid = "c2297ded-f4af-51ae-bb23-16f91089e4e1" -version = "1.2.2" +version = "1.3.0" [[deps.ZeroMQ_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "libsodium_jll"] -git-tree-sha1 = "fe5c65a526f066fb3000da137d5785d9649a8a47" +deps = ["Artifacts", "JLLWrappers", "Libdl", "libsodium_jll"] +git-tree-sha1 = "42f97fb27394378591666ab0e9cee369e6d0e1f9" uuid = "8f1865be-045e-5c20-9c9f-bfbfb0764568" -version = "4.3.4+0" +version = "4.3.5+0" [[deps.Zlib_jll]] deps = ["Libdl"] @@ -2059,10 +1987,10 @@ uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" version = "0.2.3+0" [[deps.libaom_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.4.0+0" +version = "3.9.0+0" [[deps.libass_jll]] deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] @@ -2083,9 +2011,9 @@ version = "2.0.2+0" [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "873b4f805771d3e4bafe63af759a26ea8ca84d14" +git-tree-sha1 = "d7015d2e18a5fd9a4f47de711837e980519781a4" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.42+0" +version = "1.6.43+1" [[deps.libsixel_jll]] deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] @@ -2101,15 +2029,21 @@ version = "1.0.20+0" [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" +git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.7+1" +version = "1.3.7+2" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" version = "1.52.0+1" +[[deps.oneTBB_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" +uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" +version = "2021.12.0+0" + [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/docs/literate/quickstart.jl b/docs/literate/quickstart.jl index eeda8d2f..6ed8d92a 100644 --- a/docs/literate/quickstart.jl +++ b/docs/literate/quickstart.jl @@ -27,9 +27,9 @@ T = 100 Δt = 0.1 ## use the exported gate dictionary to get the gates we need -σx = get_gate(:X) -σy = get_gate(:Y) -σz = get_gate(:Z) +σx = GATES[:X] +σy = GATES[:Y] +σz = GATES[:Z] ## define drift and drive Hamiltonians H_drift = 0.5 * σz diff --git a/docs/src/generated/quickstart.md b/docs/src/generated/quickstart.md index 6965bbfd..f12aba16 100644 --- a/docs/src/generated/quickstart.md +++ b/docs/src/generated/quickstart.md @@ -32,9 +32,9 @@ T = 100 Δt = 0.1 # use the exported gate dictionary to get the gates we need -σx = get_gate(:X) -σy = get_gate(:Y) -σz = get_gate(:Z) +σx = GATES[:X] +σy = GATES[:Y] +σz = GATES[:Z] # define drift and drive Hamiltonians H_drift = 0.5 * σz diff --git a/src/QuantumCollocation.jl b/src/QuantumCollocation.jl index 8dfed574..bf22a5a8 100644 --- a/src/QuantumCollocation.jl +++ b/src/QuantumCollocation.jl @@ -2,12 +2,16 @@ module QuantumCollocation using Reexport + +include("isomorphisms.jl") +@reexport using .Isomorphisms + +include("quantum_object_utils.jl") +@reexport using .QuantumObjectUtils + include("structure_utils.jl") @reexport using .StructureUtils -include("quantum_utils.jl") -@reexport using .QuantumUtils - include("quantum_systems.jl") @reexport using .QuantumSystems @@ -20,13 +24,13 @@ include("embedded_operators.jl") include("quantum_system_utils.jl") @reexport using .QuantumSystemUtils -include("losses.jl") +include("losses/_losses.jl") @reexport using .Losses -include("constraints.jl") +include("constraints/_constraints.jl") @reexport using .Constraints -include("objectives.jl") +include("objectives/_objectives.jl") @reexport using .Objectives include("integrators/_integrators.jl") diff --git a/src/constraints.jl b/src/constraints.jl deleted file mode 100644 index 767c9db7..00000000 --- a/src/constraints.jl +++ /dev/null @@ -1,850 +0,0 @@ -module Constraints - -export constrain! -export trajectory_constraints - -export AbstractConstraint - -export NonlinearConstraint - -export NonlinearEqualityConstraint - -export NonlinearInequalityConstraint - -export FinalFidelityConstraint -export FinalUnitaryFidelityConstraint -export FinalQuantumStateFidelityConstraint - -export ComplexModulusContraint - -export LinearConstraint - -export EqualityConstraint -export BoundsConstraint -export TimeStepBoundsConstraint -export TimeStepEqualityConstraint -export TimeStepsAllEqualConstraint -export L1SlackConstraint - -using ..StructureUtils -using ..QuantumUtils - -using TrajectoryIndexingUtils -using NamedTrajectories -using ForwardDiff -using SparseArrays -using Ipopt -using MathOptInterface -const MOI = MathOptInterface - - -abstract type AbstractConstraint end - -abstract type NonlinearConstraint <: AbstractConstraint end - - -function NonlinearConstraint(params::Dict) - return eval(params[:type])(; delete!(params, :type)...) -end - -""" - struct NonlinearEqualityConstraint - -Represents a nonlinear equality constraint. - -# Fields -- `g::Function`: the constraint function -- `∂g::Function`: the Jacobian of the constraint function -- `∂g_structure::Vector{Tuple{Int, Int}}`: the structure of the Jacobian - i.e. all non-zero entries -- `μ∂²g::Function`: the Hessian of the constraint function -- `μ∂²g_structure::Vector{Tuple{Int, Int}}`: the structure of the Hessian -- `dim::Int`: the dimension of the constraint function -- `params::Dict{Symbol, Any}`: a dictionary of parameters - -""" -struct NonlinearEqualityConstraint <: NonlinearConstraint - g::Function - ∂g::Function - ∂g_structure::Vector{Tuple{Int, Int}} - μ∂²g::Union{Nothing, Function} - μ∂²g_structure::Union{Nothing, Vector{Tuple{Int, Int}}} - dim::Int - params::Dict{Symbol, Any} -end - -""" - struct NonlinearInequalityConstraint - -Represents a nonlinear inequality constraint. - -# Fields -- `g::Function`: the constraint function -- `∂g::Function`: the Jacobian of the constraint function -- `∂g_structure::Vector{Tuple{Int, Int}}`: the structure of the Jacobian - i.e. all non-zero entries -- `μ∂²g::Function`: the Hessian of the constraint function -- `μ∂²g_structure::Vector{Tuple{Int, Int}}`: the structure of the Hessian -- `dim::Int`: the dimension of the constraint function -- `params::Dict{Symbol, Any}`: a dictionary of parameters containing additional - information about the constraint - -""" -struct NonlinearInequalityConstraint <: NonlinearConstraint - g::Function - ∂g::Function - ∂g_structure::Vector{Tuple{Int, Int}} - μ∂²g::Union{Nothing, Function} - μ∂²g_structure::Union{Nothing, Vector{Tuple{Int, Int}}} - dim::Int - params::Dict{Symbol, Any} -end - -""" - FinalFidelityConstraint() - - -Returns a NonlinearInequalityConstraint representing a constraint on the -minimum allowed fidelity. - -# Arguments -- `fidelity_function::Union{Function,Nothing}=nothing`: the fidelity function -- `value::Union{Float64,Nothing}=nothing`: the minimum fidelity value allowed - by the constraint -- `comps::Union{AbstractVector{Int},Nothing}=nothing`: the components of the - state to which the fidelity function is applied -- `goal::Union{AbstractVector{Float64},Nothing}=nothing`: the goal state -- `statedim::Union{Int,Nothing}=nothing`: the dimension of the state -- `zdim::Union{Int,Nothing}=nothing`: the dimension of a single time step of the trajectory -- `T::Union{Int,Nothing}=nothing`: the number of time steps -- `subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing`: the subspace indices of the fidelity - -""" - -function FinalFidelityConstraint(; - fidelity_function::Union{Symbol,Function,Nothing}=nothing, - value::Union{Float64,Nothing}=nothing, - comps::Union{AbstractVector{Int},Nothing}=nothing, - goal::Union{AbstractVector{Float64},Nothing}=nothing, - statedim::Union{Int,Nothing}=nothing, - zdim::Union{Int,Nothing}=nothing, - T::Union{Int,Nothing}=nothing, - subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing, - eval_hessian::Bool=true -) - @assert !isnothing(fidelity_function) "must provide a fidelity function" - @assert !isnothing(value) "must provide a fidelity value" - @assert !isnothing(comps) "must provide a list of components" - @assert !isnothing(goal) "must provide a goal state" - @assert !isnothing(statedim) "must provide a state dimension" - @assert !isnothing(zdim) "must provide a z dimension" - @assert !isnothing(T) "must provide a T" - - if fidelity_function isa Symbol - fidelity_function_symbol = fidelity_function - fidelity_function = eval(fidelity_function) - else - fidelity_function_symbol = Symbol(fidelity_function) - end - - if isnothing(subspace) - fid = x -> fidelity_function(x, goal) - else - fid = x -> fidelity_function(x, goal; subspace=subspace) - end - - @assert fid(randn(statedim)) isa Float64 "fidelity function must return a scalar" - - params = Dict{Symbol, Any}() - - if fidelity_function_symbol ∉ names(QuantumUtils) - @warn "Fidelity function :$(string(fidelity_function_symbol)) is not exported by QuantumUtils. Unable to save this constraint." - params[:type] = :FinalFidelityConstraint - params[:fidelity_function] = :not_saveable - else - params[:type] = :FinalFidelityConstraint - params[:fidelity_function] = fidelity_function_symbol - params[:value] = value - params[:comps] = comps - params[:goal] = goal - params[:statedim] = statedim - params[:zdim] = zdim - params[:T] = T - params[:subspace] = subspace - params[:eval_hessian] = eval_hessian - end - - state_slice = slice(T, comps, zdim) - - ℱ(x) = [fid(x)] - - g(Z⃗) = ℱ(Z⃗[state_slice]) .- value - - ∂ℱ(x) = ForwardDiff.jacobian(ℱ, x) - - ∂ℱ_structure = jacobian_structure(∂ℱ, statedim) - - col_offset = index(T, comps[1] - 1, zdim) - - ∂g_structure = [(i, j + col_offset) for (i, j) in ∂ℱ_structure] - - @views function ∂g(Z⃗; ipopt=true) - ∂ = spzeros(1, T * zdim) - ∂ℱ_x = ∂ℱ(Z⃗[state_slice]) - for (i, j) ∈ ∂ℱ_structure - ∂[i, j + col_offset] = ∂ℱ_x[i, j] - end - if ipopt - return [∂[i, j] for (i, j) in ∂g_structure] - else - return ∂ - end - end - - if eval_hessian - ∂²ℱ(x) = ForwardDiff.hessian(fid, x) - - ∂²ℱ_structure = hessian_of_lagrangian_structure(∂²ℱ, statedim, 1) - - μ∂²g_structure = [ij .+ col_offset for ij in ∂²ℱ_structure] - - @views function μ∂²g(Z⃗, μ; ipopt=true) - HoL = spzeros(T * zdim, T * zdim) - μ∂²ℱ = μ[1] * ∂²ℱ(Z⃗[state_slice]) - for (i, j) ∈ ∂²ℱ_structure - HoL[i + col_offset, j + col_offset] = μ∂²ℱ[i, j] - end - if ipopt - return [HoL[i, j] for (i, j) in μ∂²g_structure] - else - return HoL - end - end - - else - μ∂²g_structure = nothing - μ∂²g = nothing - end - - return NonlinearInequalityConstraint( - g, - ∂g, - ∂g_structure, - μ∂²g, - μ∂²g_structure, - 1, - params - ) -end - -""" - FinalUnitaryFidelityConstraint(statesymb::Symbol, val::Float64, traj::NamedTrajectory) - -Returns a FinalFidelityConstraint for the unitary fidelity function where statesymb -is the NamedTrajectory symbol representing the unitary. - -""" -function FinalUnitaryFidelityConstraint( - statesymb::Symbol, - val::Float64, - traj::NamedTrajectory; - subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing, - eval_hessian::Bool=true -) - @assert statesymb ∈ traj.names - return FinalFidelityConstraint(; - fidelity_function=unitary_fidelity, - value=val, - comps=traj.components[statesymb], - goal=traj.goal[statesymb], - statedim=traj.dims[statesymb], - zdim=traj.dim, - T=traj.T, - subspace=subspace, - eval_hessian=eval_hessian - ) -end - -""" - FinalQuantumStateFidelityConstraint(statesymb::Symbol, val::Float64, traj::NamedTrajectory) - -Returns a FinalFidelityConstraint for the unitary fidelity function where statesymb -is the NamedTrajectory symbol representing the unitary. - -""" -function FinalQuantumStateFidelityConstraint( - statesymb::Symbol, - val::Float64, - traj::NamedTrajectory; - kwargs... -) - @assert statesymb ∈ traj.names - return FinalFidelityConstraint(; - fidelity_function=fidelity, - value=val, - comps=traj.components[statesymb], - goal=traj.goal[statesymb], - statedim=traj.dims[statesymb], - zdim=traj.dim, - T=traj.T, - kwargs... - ) -end - - -""" - ComplexModulusContraint() - -Returns a NonlinearInequalityConstraint on the complex modulus of a complex control - -# Arguments -- `R::Union{Float64,Nothing}=nothing`: the maximum allowed complex modulus -- `comps::Union{AbstractVector{Int},Nothing}=nothing`: the components of the complex control, - both the real and imaginary parts -- `times::Union{AbstractVector{Int},Nothing}=nothing`: the times at which the constraint is applied -- `zdim::Union{Int,Nothing}=nothing`: the dimension of a single time step of the trajectory -- `T::Union{Int,Nothing}=nothing`: the number of time steps -""" -function ComplexModulusContraint(; - R::Union{Float64, Nothing}=nothing, - comps::Union{AbstractVector{Int}, Nothing}=nothing, - times::Union{AbstractVector{Int}, Nothing}=nothing, - zdim::Union{Int, Nothing}=nothing, - T::Union{Int, Nothing}=nothing, -) - @assert !isnothing(R) "must provide a value R, s.t. |z| <= R" - @assert !isnothing(comps) "must provide components of the complex number" - @assert !isnothing(times) "must provide times" - @assert !isnothing(zdim) "must provide a z dimension" - @assert !isnothing(T) "must provide a T" - - @assert length(comps) == 2 "component must represent a complex number and have dimension 2" - - params = Dict{Symbol, Any}() - - params[:type] = :ComplexModulusContraint - params[:R] = R - params[:comps] = comps - params[:times] = times - params[:zdim] = zdim - params[:T] = T - - gₜ(xₜ, yₜ) = [R^2 - xₜ^2 - yₜ^2] - ∂gₜ(xₜ, yₜ) = [-2xₜ, -2yₜ] - μₜ∂²gₜ(μₜ) = sparse([1, 2], [1, 2], [-2μₜ, -2μₜ]) - - @views function g(Z⃗) - r = zeros(length(times)) - for (i, t) ∈ enumerate(times) - zₜ = Z⃗[slice(t, comps, zdim)] - xₜ = zₜ[1] - yₜ = zₜ[2] - r[i] = gₜ(xₜ, yₜ)[1] - end - return r - end - - ∂g_structure = [] - - for (i, t) ∈ enumerate(times) - push!(∂g_structure, (i, index(t, comps[1], zdim))) - push!(∂g_structure, (i, index(t, comps[2], zdim))) - end - - @views function ∂g(Z⃗; ipopt=true) - ∂ = spzeros(length(times), zdim * T) - for (i, t) ∈ enumerate(times) - zₜ = Z⃗[slice(t, comps, zdim)] - xₜ = zₜ[1] - yₜ = zₜ[2] - ∂[i, slice(t, comps, zdim)] = ∂gₜ(xₜ, yₜ) - end - if ipopt - return [∂[i, j] for (i, j) in ∂g_structure] - else - return ∂ - end - end - - μ∂²g_structure = [] - - for t ∈ times - push!( - μ∂²g_structure, - ( - index(t, comps[1], zdim), - index(t, comps[1], zdim) - ) - ) - push!( - μ∂²g_structure, - ( - index(t, comps[2], zdim), - index(t, comps[2], zdim) - ) - ) - end - - function μ∂²g(Z⃗, μ; ipopt=true) - μ∂² = spzeros(zdim * T, zdim * T) - for (i, t) ∈ enumerate(times) - t_slice = slice(t, comps, zdim) - μ∂²[t_slice, t_slice] = μₜ∂²gₜ(μ[i]) - end - if ipopt - return [μ∂²[i, j] for (i, j) in μ∂²g_structure] - else - return μ∂² - end - end - - return NonlinearInequalityConstraint( - g, - ∂g, - ∂g_structure, - μ∂²g, - μ∂²g_structure, - length(times), - params - ) -end - -""" - ComplexModulusContraint(symb::Symbol, R::Float64, traj::NamedTrajectory) - -Returns a ComplexModulusContraint for the complex control NamedTrajector symbol -where R is the maximum allowed complex modulus. -""" -function ComplexModulusContraint( - name::Symbol, - R::Float64, - traj::NamedTrajectory; - times=1:traj.T, - name_comps=1:traj.dims[name] -) - @assert name ∈ traj.names - comps = traj.components[name][name_comps] - return ComplexModulusContraint(; - R=R, - comps=comps, - times=times, - zdim=traj.dim, - T=traj.T - ) -end - - -abstract type LinearConstraint <: AbstractConstraint end - -""" - constrain!(opt::Ipopt.Optimizer, vars::Vector{MOI.VariableIndex}, cons::Vector{LinearConstraint}, traj::NamedTrajectory; verbose=false) - -Supplies a set of LinearConstraints to IPOPT using MathOptInterface - -""" -function constrain!( - opt::Ipopt.Optimizer, - vars::Vector{MOI.VariableIndex}, - cons::Vector{LinearConstraint}, - traj::NamedTrajectory; - verbose=false -) - for con in cons - if verbose - println("applying constraint: ", con.label) - end - con(opt, vars, traj) - end -end - -""" - trajectory_constraints(traj::NamedTrajectory) - -Implements the initial and final value constraints and bounds constraints on the controls -and states as specified by traj. - -""" - -function trajectory_constraints(traj::NamedTrajectory) - cons = AbstractConstraint[] - - init_names = [] - - # add initial equality constraints - for (name, val) ∈ pairs(traj.initial) - ts = [1] - js = traj.components[name] - con_label = "initial value of $name" - eq_con = EqualityConstraint(ts, js, val, traj.dim; label=con_label) - push!(cons, eq_con) - push!(init_names, name) - end - - final_names = [] - - # add final equality constraints - for (name, val) ∈ pairs(traj.final) - ts = [traj.T] - js = traj.components[name] - con_label = "final value of $name" - eq_con = EqualityConstraint(ts, js, val, traj.dim; label=con_label) - push!(cons, eq_con) - push!(final_names, name) - end - - # add bounds constraints - for (name, bound) ∈ pairs(traj.bounds) - if name ∈ init_names && name ∈ final_names - ts = 2:traj.T-1 - elseif name ∈ init_names && !(name ∈ final_names) - ts = 2:traj.T - elseif name ∈ final_names && !(name ∈ init_names) - ts = 1:traj.T-1 - else - ts = 1:traj.T - end - js = traj.components[name] - con_label = "bounds on $name" - bounds = collect(zip(bound[1], bound[2])) - bounds_con = BoundsConstraint(ts, js, bounds, traj.dim; label=con_label) - push!(cons, bounds_con) - end - - return cons -end - - - -""" - struct EqualityConstraint - -Represents a linear equality constraint. - -# Fields -- `ts::AbstractArray{Int}`: the time steps at which the constraint is applied -- `js::AbstractArray{Int}`: the components of the trajectory at which the constraint is applied -- `vals::Vector{R}`: the values of the constraint -- `vardim::Int`: the dimension of a single time step of the trajectory -- `label::String`: a label for the constraint - -""" -struct EqualityConstraint <: LinearConstraint - ts::AbstractArray{Int} - js::AbstractArray{Int} - vals::Vector{R} where R - vardim::Int - label::String -end - -function EqualityConstraint( - t::Union{Int, AbstractArray{Int}}, - j::Union{Int, AbstractArray{Int}}, - val::Union{R, Vector{R}}, - vardim::Int; - label="unlabeled equality constraint" -) where R - - @assert !(isa(val, Vector{R}) && isa(j, Int)) - "if val is an array, j must be an array of integers" - - @assert isa(val, R) || - (isa(val, Vector{R}) && isa(j, AbstractArray{Int})) && - length(val) == length(j) """ - if j and val are both arrays, dimensions must match: - length(j) = $(length(j)) - length(val) = $(length(val)) - """ - - if isa(val, R) && isa(j, AbstractArray{Int}) - val = fill(val, length(j)) - end - - return EqualityConstraint( - [t...], - [j...], - [val...], - vardim, - label - ) -end - - -function (con::EqualityConstraint)( - opt::Ipopt.Optimizer, - vars::Vector{MOI.VariableIndex}, - traj::NamedTrajectory -) - for t in con.ts - for (j, val) in zip(con.js, con.vals) - MOI.add_constraints( - opt, - vars[index(t, j, con.vardim)], - MOI.EqualTo(val) - ) - end - end -end - -struct BoundsConstraint <: LinearConstraint - ts::AbstractArray{Int} - js::AbstractArray{Int} - vals::Vector{Tuple{R, R}} where R <: Real - vardim::Int - label::String -end - -function BoundsConstraint( - t::Union{Int, AbstractArray{Int}}, - j::Union{Int, AbstractArray{Int}}, - val::Union{Tuple{R, R}, Vector{Tuple{R, R}}}, - vardim::Int; - label="unlabeled bounds constraint" -) where R <: Real - - @assert !(isa(val, Vector{Tuple{R, R}}) && isa(j, Int)) - "if val is an array, var must be an array of integers" - - if isa(val, Tuple{R,R}) && isa(j, AbstractArray{Int}) - - val = fill(val, length(j)) - - elseif isa(val, Tuple{R, R}) && isa(j, Int) - - val = [val] - j = [j] - - end - - @assert *([v[1] <= v[2] for v in val]...) "lower bound must be less than upper bound" - - return BoundsConstraint( - [t...], - j, - val, - vardim, - label - ) -end - -function BoundsConstraint( - t::Union{Int, AbstractArray{Int}}, - j::Union{Int, AbstractArray{Int}}, - val::Union{R, Vector{R}}, - vardim::Int; - label="unlabeled bounds constraint" -) where R <: Real - - @assert !(isa(val, Vector{R}) && isa(j, Int)) - "if val is an array, var must be an array of integers" - - if isa(val, R) && isa(j, AbstractArray{Int}) - - bounds = (-abs(val), abs(val)) - val = fill(bounds, length(j)) - - elseif isa(val, R) && isa(j, Int) - - bounds = (-abs(val), abs(val)) - val = [bounds] - j = [j] - - elseif isa(val, Vector{R}) - - val = [(-abs(v), abs(v)) for v in val] - - end - - return BoundsConstraint( - [t...], - j, - val, - vardim, - label - ) -end - -function (con::BoundsConstraint)( - opt::Ipopt.Optimizer, - vars::Vector{MOI.VariableIndex}, - traj::NamedTrajectory -) - for t in con.ts - for (j, (lb, ub)) in zip(con.js, con.vals) - if !isinf(lb) - MOI.add_constraints( - opt, - vars[index(t, j, con.vardim)], - MOI.GreaterThan(lb) - ) - end - if !isinf(ub) - MOI.add_constraints( - opt, - vars[index(t, j, con.vardim)], - MOI.LessThan(ub) - ) - end - end - end -end - -struct TimeStepBoundsConstraint <: LinearConstraint - bounds::Tuple{R, R} where R <: Real - Δt_indices::AbstractVector{Int} - label::String - - function TimeStepBoundsConstraint( - bounds::Tuple{R, R} where R <: Real, - Δt_indices::AbstractVector{Int}, - T::Int; - label="time step bounds constraint" - ) - @assert bounds[1] < bounds[2] "lower bound must be less than upper bound" - return new(bounds, Δt_indices, label) - end -end - -function (con::TimeStepBoundsConstraint)( - opt::Ipopt.Optimizer, - vars::Vector{MOI.VariableIndex}, - traj::NamedTrajectory -) - for i ∈ con.Δt_indices - MOI.add_constraints( - opt, - vars[i], - MOI.GreaterThan(con.bounds[1]) - ) - MOI.add_constraints( - opt, - vars[i], - MOI.LessThan(con.bounds[2]) - ) - end -end - -struct TimeStepEqualityConstraint <: LinearConstraint - val::R where R <: Real - Δt_indices::AbstractVector{Int} - label::String - - function TimeStepEqualityConstraint( - val::R where R <: Real, - Δt_indices::AbstractVector{Int}; - label="unlabeled time step equality constraint" - ) - return new(val, Δt_indices, label) - end -end - -function (con::TimeStepEqualityConstraint)( - opt::Ipopt.Optimizer, - vars::Vector{MOI.VariableIndex}, - traj::NamedTrajectory -) - for i ∈ con.Δt_indices - MOI.add_constraints( - opt, - vars[i], - MOI.EqualTo(con.val) - ) - end -end - -struct TimeStepsAllEqualConstraint <: LinearConstraint - Δt_indices::AbstractVector{Int} - label::String - - function TimeStepsAllEqualConstraint( - Δt_indices::AbstractVector{Int}; - label="time step all equal constraint" - ) - return new(Δt_indices, label) - end - - function TimeStepsAllEqualConstraint( - Δt_symb::Symbol, - traj::NamedTrajectory; - label="time step all equal constraint" - ) - Δt_comp = traj.components[Δt_symb][1] - Δt_indices = [index(t, Δt_comp, traj.dim) for t = 1:traj.T] - return new(Δt_indices, label) - end -end - -function (con::TimeStepsAllEqualConstraint)( - opt::Ipopt.Optimizer, - vars::Vector{MOI.VariableIndex}, - traj::NamedTrajectory -) - N = length(con.Δt_indices) - for i = 1:N-1 - Δtᵢ = MOI.ScalarAffineTerm(1.0, vars[con.Δt_indices[i]]) - minusΔt̄ = MOI.ScalarAffineTerm(-1.0, vars[con.Δt_indices[end]]) - MOI.add_constraints( - opt, - MOI.ScalarAffineFunction([Δtᵢ, minusΔt̄], 0.0), - MOI.EqualTo(0.0) - ) - end -end - -struct L1SlackConstraint <: LinearConstraint - var_name::Symbol - slack_names::Vector{Symbol} - indices::AbstractVector{Int} - times::AbstractVector{Int} - label::String - - function L1SlackConstraint( - name::Symbol, - traj::NamedTrajectory; - indices=1:traj.dims[name], - times=(name ∈ keys(traj.initial) ? 2 : 1):traj.T, - label="L1 slack constraint on $name" - ) - @assert all(i ∈ 1:traj.dims[name] for i ∈ indices) - s1_name = Symbol("s1_$name") - s2_name = Symbol("s2_$name") - slack_names = [s1_name, s2_name] - add_component!(traj, s1_name, rand(length(indices), traj.T)) - add_component!(traj, s2_name, rand(length(indices), traj.T)) - return new(name, slack_names, indices, times, label) - end -end - -function (con::L1SlackConstraint)( - opt::Ipopt.Optimizer, - vars::Vector{MOI.VariableIndex}, - traj::NamedTrajectory -) - for t ∈ con.times - for (s1, s2, x) in zip( - slice(t, traj.components[con.slack_names[1]], traj.dim), - slice(t, traj.components[con.slack_names[2]], traj.dim), - slice(t, traj.components[con.var_name][con.indices], traj.dim) - ) - MOI.add_constraints( - opt, - vars[s1], - MOI.GreaterThan(0.0) - ) - MOI.add_constraints( - opt, - vars[s2], - MOI.GreaterThan(0.0) - ) - t1 = MOI.ScalarAffineTerm(1.0, vars[s1]) - t2 = MOI.ScalarAffineTerm(-1.0, vars[s2]) - t3 = MOI.ScalarAffineTerm(-1.0, vars[x]) - MOI.add_constraints( - opt, - MOI.ScalarAffineFunction([t1, t2, t3], 0.0), - MOI.EqualTo(0.0) - ) - end - end -end - -end diff --git a/src/constraints/_constraints.jl b/src/constraints/_constraints.jl new file mode 100644 index 00000000..a059ad88 --- /dev/null +++ b/src/constraints/_constraints.jl @@ -0,0 +1,129 @@ +module Constraints + +export AbstractConstraint + +export LinearConstraint +export constrain! + +export NonlinearConstraint +export NonlinearEqualityConstraint +export NonlinearInequalityConstraint + +using ..Losses +using ..Isomorphisms +using ..StructureUtils + +using TrajectoryIndexingUtils +using NamedTrajectories +using ForwardDiff +using SparseArrays +using Ipopt +using MathOptInterface +const MOI = MathOptInterface + +# TODO: +# - [ ] Do not reference the Z object in the constraint (components only / remove "name") + +# ----------------------------------------------------------------------------- # +# Abstract Constraints # +# ----------------------------------------------------------------------------- # + +abstract type AbstractConstraint end +abstract type LinearConstraint <: AbstractConstraint end +abstract type NonlinearConstraint <: AbstractConstraint end + +include("linear_trajectory_constraints.jl") +include("complex_modulus_constraint.jl") +include("fidelity_constraint.jl") +include("l1_slack_constraint.jl") + + +# ----------------------------------------------------------------------------- # +# Linear Constraint # +# ----------------------------------------------------------------------------- # + +""" + constrain!(opt::Ipopt.Optimizer, vars::Vector{MOI.VariableIndex}, cons::Vector{LinearConstraint}, traj::NamedTrajectory; verbose=false) + +Supplies a set of LinearConstraints to IPOPT using MathOptInterface + +""" +function constrain!( + opt::Ipopt.Optimizer, + vars::Vector{MOI.VariableIndex}, + cons::Vector{LinearConstraint}, + traj::NamedTrajectory; + verbose=false +) + for con in cons + if verbose + println("applying constraint: ", con.label) + end + con(opt, vars, traj) + end +end + + +# ----------------------------------------------------------------------------- # +# Nonlinear Constraint # +# ----------------------------------------------------------------------------- # + + +function NonlinearConstraint(params::Dict) + return eval(params[:type])(; delete!(params, :type)...) +end + +""" + struct NonlinearEqualityConstraint + +Represents a nonlinear equality constraint. + +# Fields +- `g::Function`: the constraint function +- `∂g::Function`: the Jacobian of the constraint function +- `∂g_structure::Vector{Tuple{Int, Int}}`: the structure of the Jacobian + i.e. all non-zero entries +- `μ∂²g::Function`: the Hessian of the constraint function +- `μ∂²g_structure::Vector{Tuple{Int, Int}}`: the structure of the Hessian +- `dim::Int`: the dimension of the constraint function +- `params::Dict{Symbol, Any}`: a dictionary of parameters + +""" +struct NonlinearEqualityConstraint <: NonlinearConstraint + g::Function + ∂g::Function + ∂g_structure::Vector{Tuple{Int, Int}} + μ∂²g::Union{Nothing, Function} + μ∂²g_structure::Union{Nothing, Vector{Tuple{Int, Int}}} + dim::Int + params::Dict{Symbol, Any} +end + +""" + struct NonlinearInequalityConstraint + +Represents a nonlinear inequality constraint. + +# Fields +- `g::Function`: the constraint function +- `∂g::Function`: the Jacobian of the constraint function +- `∂g_structure::Vector{Tuple{Int, Int}}`: the structure of the Jacobian + i.e. all non-zero entries +- `μ∂²g::Function`: the Hessian of the constraint function +- `μ∂²g_structure::Vector{Tuple{Int, Int}}`: the structure of the Hessian +- `dim::Int`: the dimension of the constraint function +- `params::Dict{Symbol, Any}`: a dictionary of parameters containing additional + information about the constraint + +""" +struct NonlinearInequalityConstraint <: NonlinearConstraint + g::Function + ∂g::Function + ∂g_structure::Vector{Tuple{Int, Int}} + μ∂²g::Union{Nothing, Function} + μ∂²g_structure::Union{Nothing, Vector{Tuple{Int, Int}}} + dim::Int + params::Dict{Symbol, Any} +end + +end diff --git a/src/constraints/complex_modulus_constraint.jl b/src/constraints/complex_modulus_constraint.jl new file mode 100644 index 00000000..9e0de5fc --- /dev/null +++ b/src/constraints/complex_modulus_constraint.jl @@ -0,0 +1,146 @@ +export ComplexModulusContraint + + +""" + ComplexModulusContraint() + +Returns a NonlinearInequalityConstraint on the complex modulus of a complex control + +TODO: Changed zdim -> dim. Constraint should be tested for global params. + +# Arguments +- `R::Union{Float64,Nothing}=nothing`: the maximum allowed complex modulus +- `comps::Union{AbstractVector{Int},Nothing}=nothing`: the components of the complex control, + both the real and imaginary parts +- `times::Union{AbstractVector{Int},Nothing}=nothing`: the times at which the constraint is applied +- `dim::Union{Int,Nothing}=nothing`: the dimension of a single time step of the trajectory +- `T::Union{Int,Nothing}=nothing`: the number of time steps +""" +function ComplexModulusContraint(; + R::Union{Float64, Nothing}=nothing, + comps::Union{AbstractVector{Int}, Nothing}=nothing, + times::Union{AbstractVector{Int}, Nothing}=nothing, + dim::Union{Int, Nothing}=nothing, + T::Union{Int, Nothing}=nothing, +) + @assert !isnothing(R) "must provide a value R, s.t. |z| <= R" + @assert !isnothing(comps) "must provide components of the complex number" + @assert !isnothing(times) "must provide times" + @assert !isnothing(dim) "must provide a trajectory dimension" + @assert !isnothing(T) "must provide a T" + + @assert length(comps) == 2 "component must represent a complex number and have dimension 2" + + params = Dict{Symbol, Any}() + + params[:type] = :ComplexModulusContraint + params[:R] = R + params[:comps] = comps + params[:times] = times + params[:dim] = dim + params[:T] = T + + gₜ(xₜ, yₜ) = [R^2 - xₜ^2 - yₜ^2] + ∂gₜ(xₜ, yₜ) = [-2xₜ, -2yₜ] + μₜ∂²gₜ(μₜ) = sparse([1, 2], [1, 2], [-2μₜ, -2μₜ]) + + @views function g(Z⃗) + r = zeros(length(times)) + for (i, t) ∈ enumerate(times) + zₜ = Z⃗[slice(t, comps, dim)] + xₜ = zₜ[1] + yₜ = zₜ[2] + r[i] = gₜ(xₜ, yₜ)[1] + end + return r + end + + ∂g_structure = [] + + for (i, t) ∈ enumerate(times) + push!(∂g_structure, (i, index(t, comps[1], dim))) + push!(∂g_structure, (i, index(t, comps[2], dim))) + end + + @views function ∂g(Z⃗; ipopt=true) + ∂ = spzeros(length(times), length(Z⃗)) + for (i, t) ∈ enumerate(times) + zₜ = Z⃗[slice(t, comps, dim)] + xₜ = zₜ[1] + yₜ = zₜ[2] + ∂[i, slice(t, comps, dim)] = ∂gₜ(xₜ, yₜ) + end + if ipopt + return [∂[i, j] for (i, j) in ∂g_structure] + else + return ∂ + end + end + + μ∂²g_structure = [] + + for t ∈ times + push!( + μ∂²g_structure, + ( + index(t, comps[1], dim), + index(t, comps[1], dim) + ) + ) + push!( + μ∂²g_structure, + ( + index(t, comps[2], dim), + index(t, comps[2], dim) + ) + ) + end + + function μ∂²g(Z⃗, μ; ipopt=true) + n = length(Z⃗) + μ∂² = spzeros(n, n) + for (i, t) ∈ enumerate(times) + t_slice = slice(t, comps, dim) + μ∂²[t_slice, t_slice] = μₜ∂²gₜ(μ[i]) + end + if ipopt + return [μ∂²[i, j] for (i, j) in μ∂²g_structure] + else + return μ∂² + end + end + + return NonlinearInequalityConstraint( + g, + ∂g, + ∂g_structure, + μ∂²g, + μ∂²g_structure, + length(times), + params + ) +end + +""" + ComplexModulusContraint(symb::Symbol, R::Float64, traj::NamedTrajectory) + +Returns a ComplexModulusContraint for the complex control NamedTrajector symbol +where R is the maximum allowed complex modulus. +""" +function ComplexModulusContraint( + name::Symbol, + R::Float64, + traj::NamedTrajectory; + times=1:traj.T, + name_comps=1:traj.dims[name] +) + @assert name ∈ traj.names + comps = traj.components[name][name_comps] + return ComplexModulusContraint(; + R=R, + comps=comps, + times=times, + zdim=traj.dim, + T=traj.T + ) +end \ No newline at end of file diff --git a/src/constraints/fidelity_constraint.jl b/src/constraints/fidelity_constraint.jl new file mode 100644 index 00000000..095b3627 --- /dev/null +++ b/src/constraints/fidelity_constraint.jl @@ -0,0 +1,287 @@ +export FinalFidelityConstraint +export FinalUnitaryFidelityConstraint +export FinalQuantumStateFidelityConstraint +export FinalUnitaryFreePhaseFidelityConstraint + +### +### FinalFidelityConstraint +### + +""" + FinalFidelityConstraint() + + +Returns a NonlinearInequalityConstraint representing a constraint on the +minimum allowed fidelity. + +# Arguments +- `fidelity_function::Union{Function,Nothing}=nothing`: the fidelity function +- `value::Union{Float64,Nothing}=nothing`: the minimum fidelity value allowed + by the constraint +- `comps::Union{AbstractVector{Int},Nothing}=nothing`: the components of the + state to which the fidelity function is applied +- `goal::Union{AbstractVector{Float64},Nothing}=nothing`: the goal state +- `statedim::Union{Int,Nothing}=nothing`: the dimension of the state +- `zdim::Union{Int,Nothing}=nothing`: the dimension of a single time step of the trajectory +- `T::Union{Int,Nothing}=nothing`: the number of time steps +- `subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing`: the subspace indices of the fidelity + +""" +function FinalFidelityConstraint(; + fidelity_function::Union{Symbol,Function,Nothing}=nothing, + value::Union{Float64,Nothing}=nothing, + comps::Union{AbstractVector{Int},Nothing}=nothing, + goal::Union{AbstractVector{Float64},Nothing}=nothing, + statedim::Union{Int,Nothing}=nothing, + zdim::Union{Int,Nothing}=nothing, + T::Union{Int,Nothing}=nothing, + subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing, + eval_hessian::Bool=true +) + @assert !isnothing(fidelity_function) "must provide a fidelity function" + @assert !isnothing(value) "must provide a fidelity value" + @assert !isnothing(comps) "must provide a list of components" + @assert !isnothing(goal) "must provide a goal state" + @assert !isnothing(statedim) "must provide a state dimension" + @assert !isnothing(zdim) "must provide a z dimension" + @assert !isnothing(T) "must provide a T" + + if fidelity_function isa Symbol + fidelity_function_symbol = fidelity_function + fidelity_function = eval(fidelity_function) + else + fidelity_function_symbol = Symbol(fidelity_function) + end + + if isnothing(subspace) + fid = x -> fidelity_function(x, goal) + else + fid = x -> fidelity_function(x, goal; subspace=subspace) + end + + @assert fid(randn(statedim)) isa Float64 "fidelity function must return a scalar" + + params = Dict{Symbol, Any}() + + if fidelity_function_symbol ∉ names(Losses) + @warn "Fidelity function :$(string(fidelity_function_symbol)) is not exported. Unable to save this constraint." + params[:type] = :FinalFidelityConstraint + params[:fidelity_function] = :not_saveable + else + params[:type] = :FinalFidelityConstraint + params[:fidelity_function] = fidelity_function_symbol + params[:value] = value + params[:comps] = comps + params[:goal] = goal + params[:statedim] = statedim + params[:zdim] = zdim + params[:T] = T + params[:subspace] = subspace + params[:eval_hessian] = eval_hessian + end + + state_slice = slice(T, comps, zdim) + + ℱ(x) = [fid(x)] + + g(Z⃗) = ℱ(Z⃗[state_slice]) .- value + + ∂ℱ(x) = ForwardDiff.jacobian(ℱ, x) + + ∂ℱ_structure = jacobian_structure(∂ℱ, statedim) + + col_offset = index(T, comps[1] - 1, zdim) + + ∂g_structure = [(i, j + col_offset) for (i, j) in ∂ℱ_structure] + + @views function ∂g(Z⃗; ipopt=true) + ∂ = spzeros(1, T * zdim) + ∂ℱ_x = ∂ℱ(Z⃗[state_slice]) + for (i, j) ∈ ∂ℱ_structure + ∂[i, j + col_offset] = ∂ℱ_x[i, j] + end + if ipopt + return [∂[i, j] for (i, j) in ∂g_structure] + else + return ∂ + end + end + + if eval_hessian + ∂²ℱ(x) = ForwardDiff.hessian(fid, x) + + ∂²ℱ_structure = hessian_of_lagrangian_structure(∂²ℱ, statedim, 1) + + μ∂²g_structure = [ij .+ col_offset for ij in ∂²ℱ_structure] + + @views function μ∂²g(Z⃗, μ; ipopt=true) + HoL = spzeros(T * zdim, T * zdim) + μ∂²ℱ = μ[1] * ∂²ℱ(Z⃗[state_slice]) + for (i, j) ∈ ∂²ℱ_structure + HoL[i + col_offset, j + col_offset] = μ∂²ℱ[i, j] + end + if ipopt + return [HoL[i, j] for (i, j) in μ∂²g_structure] + else + return HoL + end + end + + else + μ∂²g_structure = nothing + μ∂²g = nothing + end + + return NonlinearInequalityConstraint( + g, + ∂g, + ∂g_structure, + μ∂²g, + μ∂²g_structure, + 1, + params + ) +end + +### +### FinalUnitaryFidelityConstraint +### + +""" + FinalUnitaryFidelityConstraint(statesymb::Symbol, val::Float64, traj::NamedTrajectory) + +Returns a FinalFidelityConstraint for the unitary fidelity function where statesymb +is the NamedTrajectory symbol representing the unitary. + +""" +function FinalUnitaryFidelityConstraint( + statesymb::Symbol, + val::Float64, + traj::NamedTrajectory; + subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing, + eval_hessian::Bool=true +) + @assert statesymb ∈ traj.names + return FinalFidelityConstraint(; + fidelity_function=iso_vec_unitary_fidelity, + value=val, + comps=traj.components[statesymb], + goal=traj.goal[statesymb], + statedim=traj.dims[statesymb], + zdim=traj.dim, + T=traj.T, + subspace=subspace, + eval_hessian=eval_hessian + ) +end + +### +### FinalQuantumStateFidelityConstraint +### + +""" + FinalQuantumStateFidelityConstraint(statesymb::Symbol, val::Float64, traj::NamedTrajectory) + +Returns a FinalFidelityConstraint for the unitary fidelity function where statesymb +is the NamedTrajectory symbol representing the unitary. + +""" +function FinalQuantumStateFidelityConstraint( + statesymb::Symbol, + val::Float64, + traj::NamedTrajectory; + kwargs... +) + @assert statesymb ∈ traj.names + return FinalFidelityConstraint(; + fidelity_function=fidelity, + value=val, + comps=traj.components[statesymb], + goal=traj.goal[statesymb], + statedim=traj.dims[statesymb], + zdim=traj.dim, + T=traj.T, + kwargs... + ) +end + +### +### FinalUnitaryFreePhaseFidelityConstraint +### + +""" + FinalUnitaryFreePhaseFidelityConstraint + +Returns a NonlinearInequalityConstraint representing a constraint on the minimum allowed fidelity +for a free phase unitary. + +""" +function FinalUnitaryFreePhaseFidelityConstraint(; + value::Union{Float64,Nothing}=nothing, + state_slice::Union{AbstractVector{Int},Nothing}=nothing, + phase_slice::Union{AbstractVector{Int},Nothing}=nothing, + goal::Union{AbstractVector{Float64},Nothing}=nothing, + phase_operators::Union{AbstractVector{<:AbstractMatrix{<:Complex{Float64}}},Nothing}=nothing, + zdim::Union{Int,Nothing}=nothing, + subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing, + eval_hessian::Bool=false +) + @assert !isnothing(value) "must provide a fidelity value" + @assert !isnothing(state_slice) "must provide state_slice" + @assert !isnothing(phase_slice) "must provide phase_slice" + @assert !isnothing(goal) "must provide a goal state" + @assert !isnothing(zdim) "must provide a z dimension" + + loss = :UnitaryFreePhaseInfidelityLoss + ℱ = eval(loss)(goal, phase_operators; subspace=subspace) + + params = Dict( + :type => :FinalUnitaryFreePhaseFidelityConstraint, + :loss => loss, + :value => value, + :state_slice => state_slice, + :phase_slice => phase_slice, + :goal => goal, + :phase_operators => phase_operators, + :subspace => subspace, + :eval_hessian => eval_hessian, + ) + + @views function g(Z⃗) + Ũ⃗ = Z⃗[state_slice] + ϕ⃗ = Z⃗[phase_slice] + return [(1 - value) - ℱ(Ũ⃗, ϕ⃗)] + end + + ∂g_structure = [(1, j) for j ∈ [state_slice; phase_slice]] + + @views function ∂g(Z⃗; ipopt=true) + Ũ⃗ = Z⃗[state_slice] + ϕ⃗ = Z⃗[phase_slice] + ∂ = -ℱ(Ũ⃗, ϕ⃗; gradient=true) + + if ipopt + return ∂ + else + ∂_fill = spzeros(1, zdim) + for (∂ᵢⱼ, (i, j)) in zip(∂, ∂g_structure) + ∂_fill[i, j] = ∂ᵢⱼ + end + return ∂_fill + end + end + + # Hessian + μ∂²g_structure = [] + μ∂²g = nothing + + return NonlinearInequalityConstraint( + g, + ∂g, + ∂g_structure, + μ∂²g, + μ∂²g_structure, + 1, + params + ) +end \ No newline at end of file diff --git a/src/constraints/l1_slack_constraint.jl b/src/constraints/l1_slack_constraint.jl new file mode 100644 index 00000000..e852e00f --- /dev/null +++ b/src/constraints/l1_slack_constraint.jl @@ -0,0 +1,59 @@ +export L1SlackConstraint + + +struct L1SlackConstraint <: LinearConstraint + var_name::Symbol + slack_names::Vector{Symbol} + indices::AbstractVector{Int} + times::AbstractVector{Int} + label::String + + function L1SlackConstraint( + name::Symbol, + traj::NamedTrajectory; + indices=1:traj.dims[name], + times=(name ∈ keys(traj.initial) ? 2 : 1):traj.T, + label="L1 slack constraint on $name" + ) + @assert all(i ∈ 1:traj.dims[name] for i ∈ indices) + s1_name = Symbol("s1_$name") + s2_name = Symbol("s2_$name") + slack_names = [s1_name, s2_name] + add_component!(traj, s1_name, rand(length(indices), traj.T)) + add_component!(traj, s2_name, rand(length(indices), traj.T)) + return new(name, slack_names, indices, times, label) + end +end + +function (con::L1SlackConstraint)( + opt::Ipopt.Optimizer, + vars::Vector{MOI.VariableIndex}, + traj::NamedTrajectory +) + for t ∈ con.times + for (s1, s2, x) in zip( + slice(t, traj.components[con.slack_names[1]], traj.dim), + slice(t, traj.components[con.slack_names[2]], traj.dim), + slice(t, traj.components[con.var_name][con.indices], traj.dim) + ) + MOI.add_constraints( + opt, + vars[s1], + MOI.GreaterThan(0.0) + ) + MOI.add_constraints( + opt, + vars[s2], + MOI.GreaterThan(0.0) + ) + t1 = MOI.ScalarAffineTerm(1.0, vars[s1]) + t2 = MOI.ScalarAffineTerm(-1.0, vars[s2]) + t3 = MOI.ScalarAffineTerm(-1.0, vars[x]) + MOI.add_constraints( + opt, + MOI.ScalarAffineFunction([t1, t2, t3], 0.0), + MOI.EqualTo(0.0) + ) + end + end +end \ No newline at end of file diff --git a/src/constraints/linear_trajectory_constraints.jl b/src/constraints/linear_trajectory_constraints.jl new file mode 100644 index 00000000..a99e08d3 --- /dev/null +++ b/src/constraints/linear_trajectory_constraints.jl @@ -0,0 +1,349 @@ +export trajectory_constraints + +export EqualityConstraint +export BoundsConstraint +export TimeStepBoundsConstraint +export TimeStepEqualityConstraint +export TimeStepsAllEqualConstraint + +""" + trajectory_constraints(traj::NamedTrajectory) + +Implements the initial and final value constraints and bounds constraints on the controls +and states as specified by traj. + +""" +function trajectory_constraints(traj::NamedTrajectory) + cons = AbstractConstraint[] + + init_names = [] + + # add initial equality constraints + for (name, val) ∈ pairs(traj.initial) + ts = [1] + js = traj.components[name] + con_label = "initial value of $name" + eq_con = EqualityConstraint(ts, js, val, traj.dim; label=con_label) + push!(cons, eq_con) + push!(init_names, name) + end + + final_names = [] + + # add final equality constraints + for (name, val) ∈ pairs(traj.final) + ts = [traj.T] + js = traj.components[name] + con_label = "final value of $name" + eq_con = EqualityConstraint(ts, js, val, traj.dim; label=con_label) + push!(cons, eq_con) + push!(final_names, name) + end + + # add bounds constraints + for (name, bound) ∈ pairs(traj.bounds) + if name ∈ init_names && name ∈ final_names + ts = 2:traj.T-1 + elseif name ∈ init_names && !(name ∈ final_names) + ts = 2:traj.T + elseif name ∈ final_names && !(name ∈ init_names) + ts = 1:traj.T-1 + else + ts = 1:traj.T + end + js = traj.components[name] + con_label = "bounds on $name" + bounds = collect(zip(bound[1], bound[2])) + bounds_con = BoundsConstraint(ts, js, bounds, traj.dim; label=con_label) + push!(cons, bounds_con) + end + + return cons +end + +### +### EqualityConstraint +### + +""" + struct EqualityConstraint + +Represents a linear equality constraint. + +# Fields +- `ts::AbstractArray{Int}`: the time steps at which the constraint is applied +- `js::AbstractArray{Int}`: the components of the trajectory at which the constraint is applied +- `vals::Vector{R}`: the values of the constraint +- `vardim::Int`: the dimension of a single time step of the trajectory +- `label::String`: a label for the constraint + +""" +struct EqualityConstraint <: LinearConstraint + ts::AbstractArray{Int} + js::AbstractArray{Int} + vals::Vector{R} where R + vardim::Int + label::String +end + +function EqualityConstraint( + t::Union{Int, AbstractArray{Int}}, + j::Union{Int, AbstractArray{Int}}, + val::Union{R, Vector{R}}, + vardim::Int; + label="unlabeled equality constraint" +) where R + + @assert !(isa(val, Vector{R}) && isa(j, Int)) + "if val is an array, j must be an array of integers" + + @assert isa(val, R) || + (isa(val, Vector{R}) && isa(j, AbstractArray{Int})) && + length(val) == length(j) """ + if j and val are both arrays, dimensions must match: + length(j) = $(length(j)) + length(val) = $(length(val)) + """ + + if isa(val, R) && isa(j, AbstractArray{Int}) + val = fill(val, length(j)) + end + + return EqualityConstraint( + [t...], + [j...], + [val...], + vardim, + label + ) +end + + +function (con::EqualityConstraint)( + opt::Ipopt.Optimizer, + vars::Vector{MOI.VariableIndex}, + traj::NamedTrajectory +) + for t in con.ts + for (j, val) in zip(con.js, con.vals) + MOI.add_constraints( + opt, + vars[index(t, j, con.vardim)], + MOI.EqualTo(val) + ) + end + end +end + +### +### BoundsConstraint +### + +struct BoundsConstraint <: LinearConstraint + ts::AbstractArray{Int} + js::AbstractArray{Int} + vals::Vector{Tuple{R, R}} where R <: Real + vardim::Int + label::String +end + +function BoundsConstraint( + t::Union{Int, AbstractArray{Int}}, + j::Union{Int, AbstractArray{Int}}, + val::Union{Tuple{R, R}, Vector{Tuple{R, R}}}, + vardim::Int; + label="unlabeled bounds constraint" +) where R <: Real + + @assert !(isa(val, Vector{Tuple{R, R}}) && isa(j, Int)) + "if val is an array, var must be an array of integers" + + if isa(val, Tuple{R,R}) && isa(j, AbstractArray{Int}) + + val = fill(val, length(j)) + + elseif isa(val, Tuple{R, R}) && isa(j, Int) + + val = [val] + j = [j] + + end + + @assert *([v[1] <= v[2] for v in val]...) "lower bound must be less than upper bound" + + return BoundsConstraint( + [t...], + j, + val, + vardim, + label + ) +end + +function BoundsConstraint( + t::Union{Int, AbstractArray{Int}}, + j::Union{Int, AbstractArray{Int}}, + val::Union{R, Vector{R}}, + vardim::Int; + label="unlabeled bounds constraint" +) where R <: Real + + @assert !(isa(val, Vector{R}) && isa(j, Int)) + "if val is an array, var must be an array of integers" + + if isa(val, R) && isa(j, AbstractArray{Int}) + + bounds = (-abs(val), abs(val)) + val = fill(bounds, length(j)) + + elseif isa(val, R) && isa(j, Int) + + bounds = (-abs(val), abs(val)) + val = [bounds] + j = [j] + + elseif isa(val, Vector{R}) + + val = [(-abs(v), abs(v)) for v in val] + + end + + return BoundsConstraint( + [t...], + j, + val, + vardim, + label + ) +end + +function (con::BoundsConstraint)( + opt::Ipopt.Optimizer, + vars::Vector{MOI.VariableIndex}, + traj::NamedTrajectory +) + for t in con.ts + for (j, (lb, ub)) in zip(con.js, con.vals) + MOI.add_constraints( + opt, + vars[index(t, j, con.vardim)], + MOI.GreaterThan(lb) + ) + MOI.add_constraints( + opt, + vars[index(t, j, con.vardim)], + MOI.LessThan(ub) + ) + end + end +end + +### +### TimeStepBoundsConstraint +### + +struct TimeStepBoundsConstraint <: LinearConstraint + bounds::Tuple{R, R} where R <: Real + Δt_indices::AbstractVector{Int} + label::String + + function TimeStepBoundsConstraint( + bounds::Tuple{R, R} where R <: Real, + Δt_indices::AbstractVector{Int}, + T::Int; + label="time step bounds constraint" + ) + @assert bounds[1] < bounds[2] "lower bound must be less than upper bound" + return new(bounds, Δt_indices, label) + end +end + +function (con::TimeStepBoundsConstraint)( + opt::Ipopt.Optimizer, + vars::Vector{MOI.VariableIndex}, + traj::NamedTrajectory +) + for i ∈ con.Δt_indices + MOI.add_constraints( + opt, + vars[i], + MOI.GreaterThan(con.bounds[1]) + ) + MOI.add_constraints( + opt, + vars[i], + MOI.LessThan(con.bounds[2]) + ) + end +end + +### +### TimeStepEqualityConstraint +### + +struct TimeStepEqualityConstraint <: LinearConstraint + val::R where R <: Real + Δt_indices::AbstractVector{Int} + label::String + + function TimeStepEqualityConstraint( + val::R where R <: Real, + Δt_indices::AbstractVector{Int}; + label="unlabeled time step equality constraint" + ) + return new(val, Δt_indices, label) + end +end + +function (con::TimeStepEqualityConstraint)( + opt::Ipopt.Optimizer, + vars::Vector{MOI.VariableIndex}, + traj::NamedTrajectory +) + for i ∈ con.Δt_indices + MOI.add_constraints( + opt, + vars[i], + MOI.EqualTo(con.val) + ) + end +end + +struct TimeStepsAllEqualConstraint <: LinearConstraint + Δt_indices::AbstractVector{Int} + label::String + + function TimeStepsAllEqualConstraint( + Δt_indices::AbstractVector{Int}; + label="time step all equal constraint" + ) + return new(Δt_indices, label) + end + + function TimeStepsAllEqualConstraint( + Δt_symb::Symbol, + traj::NamedTrajectory; + label="time step all equal constraint" + ) + Δt_comp = traj.components[Δt_symb][1] + Δt_indices = [index(t, Δt_comp, traj.dim) for t = 1:traj.T] + return new(Δt_indices, label) + end +end + +function (con::TimeStepsAllEqualConstraint)( + opt::Ipopt.Optimizer, + vars::Vector{MOI.VariableIndex}, + traj::NamedTrajectory +) + N = length(con.Δt_indices) + for i = 1:N-1 + Δtᵢ = MOI.ScalarAffineTerm(1.0, vars[con.Δt_indices[i]]) + minusΔt̄ = MOI.ScalarAffineTerm(-1.0, vars[con.Δt_indices[end]]) + MOI.add_constraints( + opt, + MOI.ScalarAffineFunction([Δtᵢ, minusΔt̄], 0.0), + MOI.EqualTo(0.0) + ) + end +end \ No newline at end of file diff --git a/src/direct_sums.jl b/src/direct_sums.jl index 194025c7..8496fc94 100644 --- a/src/direct_sums.jl +++ b/src/direct_sums.jl @@ -2,17 +2,20 @@ module DirectSums export add_suffix export get_suffix +export get_suffix_label export direct_sum export merge_outer using ..Integrators using ..Problems using ..QuantumSystems -using ..QuantumUtils +using ..Isomorphisms using ..Objectives -using NamedTrajectories using SparseArrays +using TestItemRunner + +using NamedTrajectories """ @@ -67,12 +70,12 @@ direct_sum(systems::AbstractVector{<:QuantumSystem}) = reduce(direct_sum, system """ direct_sum(traj1::NamedTrajectory, traj2::NamedTrajectory) -Returns the direct sum of two `NamedTrajectory` objects. +Returns the direct sum of two `NamedTrajectory` objects. -The `NamedTrajectory` objects must have the same timestep. However, a direct sum -can return a free time problem by passing the keyword argument `free_time=true`. +The `NamedTrajectory` objects must have the same timestep. However, a direct sum +can return a free time problem by passing the keyword argument `free_time=true`. In this case, the timestep symbol must be provided. If a free time problem with more -than two trajectories is desired, the `reduce` function has been written to handle calls +than two trajectories is desired, the `reduce` function has been written to handle calls to direct sums of `NamedTrajectory` objects; simply pass the keyword argument `free_time=true` to the `reduce` function. @@ -83,7 +86,7 @@ to the `reduce` function. - `timestep_symbol::Symbol=:Δt`: The timestep symbol to use for free time problems. """ function direct_sum( - traj1::NamedTrajectory, + traj1::NamedTrajectory, traj2::NamedTrajectory; free_time::Bool=false, timestep_symbol::Symbol=:Δt, @@ -116,12 +119,12 @@ function direct_sum( # collect component data component_names = [vcat(traj.state_names..., traj.control_names...) for traj ∈ trajs] components = merge_outer([get_components(names, traj) for (names, traj) ∈ zip(component_names, trajs)]) - + # add timestep to components if free_time components = merge_outer(components, NamedTuple{(timestep_symbol,)}([get_timesteps(trajs[1])])) end - + return NamedTrajectory( components, controls=merge_outer([traj.control_names for traj in trajs]), @@ -133,19 +136,15 @@ function direct_sum( ) end -Base.endswith(symb::Symbol, suffix::AbstractString) = endswith(String(symb), suffix) -Base.endswith(integrator::UnitaryPadeIntegrator, suffix::String) = endswith(integrator.unitary_symb, suffix) -Base.endswith(integrator::DerivativeIntegrator, suffix::String) = endswith(integrator.variable, suffix) -Base.startswith(symb::Symbol, prefix::AbstractString) = startswith(String(symb), prefix) -Base.startswith(symb::Symbol, prefix::Symbol) = startswith(String(symb), String(prefix)) - # Add suffix utilities # ----------------------- +Base.startswith(symb::Symbol, prefix::AbstractString) = startswith(String(symb), prefix) +Base.startswith(symb::Symbol, prefix::Symbol) = startswith(String(symb), String(prefix)) add_suffix(symb::Symbol, suffix::String) = Symbol(string(symb, suffix)) -add_suffix(symbs::Tuple, suffix::String; exclude::AbstractVector{<:Symbol}=Symbol[]) = +add_suffix(symbs::Tuple, suffix::String; exclude::AbstractVector{<:Symbol}=Symbol[]) = Tuple(s ∈ exclude ? s : add_suffix(s, suffix) for s ∈ symbs) -add_suffix(symbs::AbstractVector, suffix::String; exclude::AbstractVector{<:Symbol}=Symbol[]) = +add_suffix(symbs::AbstractVector, suffix::String; exclude::AbstractVector{<:Symbol}=Symbol[]) = [s ∈ exclude ? s : add_suffix(s, suffix) for s ∈ symbs] add_suffix(d::Dict{Symbol, Any}, suffix::String; exclude::AbstractVector{<:Symbol}=Symbol[]) = typeof(d)(k ∈ exclude ? k : add_suffix(k, suffix) => v for (k, v) ∈ d) @@ -176,12 +175,6 @@ function add_suffix(traj::NamedTrajectory, suffix::String) ) end -add_suffix(integrator::AbstractIntegrator, suffix::String) = - modify_integrator_suffix(integrator, suffix, add_suffix) - -add_suffix(integrators::AbstractVector{<:AbstractIntegrator}, suffix::String) = - [add_suffix(integrator, suffix) for integrator ∈ integrators] - function add_suffix(sys::QuantumSystem, suffix::String) return QuantumSystem( sys.H_drift, @@ -190,40 +183,19 @@ function add_suffix(sys::QuantumSystem, suffix::String) ) end -# Special integrator routines -# --------------------------- +# get suffix label utilities +# -------------------- -function modify_integrator_suffix( - integrator::UnitaryPadeIntegrator, - suffix::String, - modifier::Function -) - # Just need the matrices - sys = QuantumSystem( - QuantumSystems.H(integrator.G_drift), - QuantumSystems.H.(integrator.G_drives) - ) - return UnitaryPadeIntegrator( - sys, - modifier(integrator.unitary_symb, suffix), - modifier(integrator.drive_symb, suffix), - order=integrator.order, - autodiff=integrator.autodiff, - G=integrator.G - ) +function get_suffix_label(s::String, pre::String)::String + if startswith(s, pre) + return chop(s, head=length(pre), tail=0) + else + error("Prefix '$pre' not found at the start of '$s'") + end end -function modify_integrator_suffix( - integrator::DerivativeIntegrator, - suffix::String, - modifier::Function -) - return DerivativeIntegrator( - modifier(integrator.variable, suffix), - modifier(integrator.derivative, suffix), - integrator.dim - ) -end +get_suffix_label(symb::Symbol, pre::Symbol) = get_suffix_label(String(symb), String(pre)) + # remove suffix utilities # ----------------------- @@ -249,11 +221,106 @@ function remove_suffix(nt::NamedTuple, suffix::String; exclude::AbstractVector{< return NamedTuple{symbs}(values(nt)) end -remove_suffix(integrator::AbstractIntegrator, suffix::String) = - modify_integrator_suffix(integrator, suffix, remove_suffix) +# Special integrator routines +# --------------------------- + +function modify_integrator_suffix( + modifier::Function, + integrator::AbstractIntegrator, + sys::AbstractQuantumSystem, + traj::NamedTrajectory, + mod_traj::NamedTrajectory, + suffix::String +) + if integrator isa UnitaryExponentialIntegrator + unitary_name = get_component_names(traj, integrator.unitary_components) + drive_name = get_component_names(traj, integrator.drive_components) + return integrator( + sys, + mod_traj, + unitary_name=modifier(unitary_name, suffix), + drive_name=modifier(drive_name, suffix) + ) + elseif integrator isa QuantumStateExponentialIntegrator + state_name = get_component_names(traj, integrator.state_components) + drive_name = get_component_names(traj, integrator.drive_components) + return integrator( + sys, + mod_traj, + state_name=modifier(state_name, suffix), + drive_name=modifier(drive_name, suffix) + ) + elseif integrator isa UnitaryPadeIntegrator + unitary_name = get_component_names(traj, integrator.unitary_components) + drive_name = get_component_names(traj, integrator.drive_components) + return integrator( + sys, + mod_traj, + unitary_name=modifier(unitary_name, suffix), + drive_name=modifier(drive_name, suffix) + ) + elseif integrator isa QuantumStatePadeIntegrator + state_name = get_component_names(traj, integrator.state_components) + drive_name = get_component_names(traj, integrator.drive_components) + return integrator( + sys, + mod_traj, + state_name=modifier(state_name, suffix), + drive_name=modifier(drive_name, suffix) + ) + elseif integrator isa DerivativeIntegrator + variable = get_component_names(traj, integrator.variable_components) + derivative = get_component_names(traj, integrator.derivative_components) + return integrator( + mod_traj, + variable=modifier(variable, suffix), + derivative=modifier(derivative, suffix) + ) + else + error("Integrator type not recognized") + end +end + +function add_suffix( + integrator::AbstractIntegrator, + sys::AbstractQuantumSystem, + traj::NamedTrajectory, + mod_traj::NamedTrajectory, + suffix::String +) + return modify_integrator_suffix(add_suffix, integrator, sys, traj, mod_traj, suffix) +end + +function add_suffix( + integrators::AbstractVector{<:AbstractIntegrator}, + sys::AbstractQuantumSystem, + traj::NamedTrajectory, + mod_traj::NamedTrajectory, + suffix::String +) + return [add_suffix(intg, sys, traj, mod_traj, suffix) for intg in integrators] +end + +function remove_suffix( + integrator::AbstractIntegrator, + sys::AbstractQuantumSystem, + traj::NamedTrajectory, + mod_traj::NamedTrajectory, + suffix::String +) + return modify_integrator_suffix(remove_suffix, integrator, sys, traj, mod_traj, suffix) +end + +function remove_suffix( + integrators::AbstractVector{<:AbstractIntegrator}, + sys::AbstractQuantumSystem, + traj::NamedTrajectory, + mod_traj::NamedTrajectory, + suffix::String +) + return [remove_suffix(intg, sys, traj, mod_traj, suffix) for intg in integrators] +end -remove_suffix(integrators::AbstractVector{<:AbstractIntegrator}, suffix::String) = - [remove_suffix(integrator, suffix) for integrator ∈ integrators] # Merge utilities # --------------- @@ -299,6 +366,27 @@ end # Get suffix utilities # -------------------- +Base.endswith(symb::Symbol, suffix::AbstractString) = endswith(String(symb), suffix) +Base.endswith(integrator::UnitaryPadeIntegrator, suffix::String) = endswith(integrator.unitary_symb, suffix) +Base.endswith(integrator::DerivativeIntegrator, suffix::String) = endswith(integrator.variable, suffix) + +function Base.endswith(integrator::AbstractIntegrator, traj::NamedTrajectory, suffix::String) + if integrator isa UnitaryExponentialIntegrator + name = get_component_names(traj, integrator.unitary_components) + elseif integrator isa QuantumStateExponentialIntegrator + name = get_component_names(traj, integrator.state_components) + elseif integrator isa UnitaryPadeIntegrator + name = get_component_names(traj, integrator.unitary_components) + elseif integrator isa QuantumStatePadeIntegrator + name = get_component_names(traj, integrator.state_components) + elseif integrator isa DerivativeIntegrator + name = get_component_names(traj, integrator.variable_components) + else + error("Integrator type not recognized") + end + return endswith(name, suffix) +end + function get_suffix(nt::NamedTuple, suffix::String; remove::Bool=false) names = Tuple(remove ? remove_suffix(k, suffix) : k for (k, v) ∈ pairs(nt) if endswith(k, suffix)) values = [v for (k, v) ∈ pairs(nt) if endswith(k, suffix)] @@ -347,11 +435,17 @@ function get_suffix(traj::NamedTrajectory, suffix::String; remove::Bool=false) ) end -function get_suffix(integrators::AbstractVector{<:AbstractIntegrator}, suffix::String; remove::Bool=false) +function get_suffix( + integrators::AbstractVector{<:AbstractIntegrator}, + sys::AbstractQuantumSystem, + traj::NamedTrajectory, + mod_traj::NamedTrajectory, + suffix::String +) found = AbstractIntegrator[] for integrator ∈ integrators - if endswith(integrator, suffix) - push!(found, remove ? remove_suffix(integrator, suffix) : deepcopy(integrator)) + if endswith(integrator, traj, suffix) + push!(found, remove_suffix(integrator, sys, traj, mod_traj, suffix)) end end return found @@ -359,6 +453,7 @@ end function get_suffix( prob::QuantumControlProblem, + subproblem_traj::NamedTrajectory, suffix::String; unitary_prefix::Symbol=:Ũ⃗, remove::Bool=false, @@ -367,9 +462,10 @@ function get_suffix( traj = get_suffix(prob.trajectory, suffix, remove=remove) # Extract the integrators - integrators = get_suffix(prob.integrators, suffix, remove=remove) - + integrators = get_suffix(prob.integrators, prob.system, prob.trajectory, subproblem_traj, suffix) + # Get direct sum indices + # TODO: Should have separate utility function # TODO: doesn't exclude more than one match i₀ = 0 indices = Int[] @@ -403,4 +499,192 @@ function get_suffix( ) end -end # module \ No newline at end of file +# =========================================================================== # + +@testitem "Apply suffix to trajectories" begin + using NamedTrajectories + include("../test/test_utils.jl") + + traj = named_trajectory_type_1(free_time=false) + suffix = "_new" + new_traj = add_suffix(traj, suffix) + + @test new_traj.state_names == add_suffix(traj.state_names, suffix) + @test new_traj.control_names == add_suffix(traj.control_names, suffix) + + same_traj = add_suffix(traj, "") + @test traj == same_traj +end + +@testitem "Merge trajectories" begin + using NamedTrajectories + include("../test/test_utils.jl") + + traj = named_trajectory_type_1(free_time=false) + + # apply suffix + pf_traj1 = add_suffix(traj, "_1") + pf_traj2 = add_suffix(traj, "_2") + + # merge + new_traj = direct_sum(pf_traj1, pf_traj2) + + @test issetequal(new_traj.state_names, vcat(pf_traj1.state_names..., pf_traj2.state_names...)) + @test issetequal(new_traj.control_names, vcat(pf_traj1.control_names..., pf_traj2.control_names...)) + + # merge2 + new_traj2 = direct_sum([pf_traj1, pf_traj2]) + + @test new_traj == new_traj2 +end + +@testitem "Merge free time trajectories" begin + using NamedTrajectories + include("../test/test_utils.jl") + + traj = named_trajectory_type_1(free_time=false) + + # apply suffix + pf_traj1 = add_suffix(traj, "_1") + pf_traj2 = add_suffix(traj, "_2") + pf_traj3 = add_suffix(traj, "_3") + state_names = vcat(pf_traj1.state_names..., pf_traj2.state_names..., pf_traj3.state_names...) + control_names = vcat(pf_traj1.control_names..., pf_traj2.control_names..., pf_traj3.control_names...) + + # merge (without reduce) + new_traj_1 = direct_sum(direct_sum(pf_traj1, pf_traj2), pf_traj3, free_time=true) + @test new_traj_1.timestep isa Symbol + @test issetequal(new_traj_1.state_names, state_names) + @test issetequal(setdiff(new_traj_1.control_names, control_names), [new_traj_1.timestep]) + + # merge (with reduce) + new_traj_2 = direct_sum([pf_traj1, pf_traj2, pf_traj3], free_time=true) + @test new_traj_2.timestep isa Symbol + @test issetequal(new_traj_2.state_names, state_names) + @test issetequal(setdiff(new_traj_2.control_names, control_names), [new_traj_2.timestep]) + + # check equality + for c in new_traj_1.control_names + @test new_traj_1[c] == new_traj_2[c] + end + for s in new_traj_1.state_names + @test new_traj_1[s] == new_traj_2[s] + end +end + +@testitem "Merge systems" begin + using NamedTrajectories + include("../test/test_utils.jl") + + H_drift = 0.01 * GATES[:Z] + H_drives = [GATES[:X], GATES[:Y]] + T = 50 + sys = QuantumSystem(H_drift, H_drives, params=Dict(:T=>T)) + + # apply suffix and sum + sys2 = direct_sum( + add_suffix(sys, "_1"), + add_suffix(sys, "_2") + ) + + @test length(sys2.H_drives) == 4 + @test sys2.params[:T_1] == T + @test sys2.params[:T_2] == T + + # add another system + sys = QuantumSystem(H_drift, H_drives, params=Dict(:T=>T, :S=>2T)) + sys3 = direct_sum(sys2, add_suffix(sys, "_3")) + @test length(sys3.H_drives) == 6 + @test sys3.params[:T_3] == T + @test sys3.params[:S_3] == 2T +end + +# TODO: fix broken test +@testitem "Get suffix" begin + @test_broken false + + # using NamedTrajectories + + # sys = QuantumSystem(0.01 * GATES[:Z], [GATES[:X], GATES[:Y]]) + # T = 50 + # Δt = 0.2 + # ip_ops = IpoptOptions(print_level=1) + # pi_ops = PiccoloOptions(verbose=false, free_time=false) + # prob1 = UnitarySmoothPulseProblem(sys, GATES[:X], T, Δt, piccolo_options=pi_ops, ipopt_options=ip_ops) + # prob2 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, piccolo_options=pi_ops, ipopt_options=ip_ops) + + # # Direct sum problem with suffix extraction + # # Note: Turn off control reset + # direct_sum_prob = UnitaryDirectSumProblem([prob1, prob2], 0.99, drive_reset_ratio=0.0, ipopt_options=ip_ops) + # # TODO: BROKEN HERE + # prob1_got = get_suffix(direct_sum_prob, "1") + # @test prob1_got.trajectory == add_suffix(prob1.trajectory, "1") + + # # Mutate the direct sum problem + # update!(prob1_got.trajectory, :a1, ones(size(prob1_got.trajectory[:a1]))) + # @test prob1_got.trajectory != add_suffix(prob1.trajectory, "1") + + # # Remove suffix during extraction + # prob1_got_without = get_suffix(direct_sum_prob, "1", remove=true) + # @test prob1_got_without.trajectory == prob1.trajectory +end + +# TODO: fix broken test +@testitem "Append to default integrators" begin + @test_broken false + # sys = QuantumSystem(0.01 * GATES[:Z], [GATES[:Y]]) + # T = 50 + # Δt = 0.2 + # ip_ops = IpoptOptions(print_level=1) + # pi_false_ops = PiccoloOptions(verbose=false, free_time=false) + # pi_true_ops = PiccoloOptions(verbose=false, free_time=true) + # prob1 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, piccolo_options=pi_false_ops, ipopt_options=ip_ops) + # prob2 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, piccolo_options=pi_true_ops, ipopt_options=ip_ops) + + # suffix = "_new" + # # UnitaryPadeIntegrator + # # TODO: BROKEN HERE + # prob1_new = add_suffix(prob1.integrators, suffix) + # @test prob1_new[1].unitary_symb == add_suffix(prob1.integrators[1].unitary_symb, suffix) + # @test prob1_new[1].drive_symb == add_suffix(prob1.integrators[1].drive_symb, suffix) + + # # DerivativeIntegrator + # @test prob1_new[2].variable == add_suffix(prob1.integrators[2].variable, suffix) + + # # UnitaryPadeIntegrator with free time + # prob2_new = add_suffix(prob2.integrators, suffix) + # @test prob2_new[1].unitary_symb == add_suffix(prob2.integrators[1].unitary_symb, suffix) + # @test prob2_new[1].drive_symb == add_suffix(prob2.integrators[1].drive_symb, suffix) + + # # DerivativeIntegrator + # @test prob2_new[2].variable == add_suffix(prob2.integrators[2].variable, suffix) +end + +@testitem "Free time get suffix" begin + using NamedTrajectories + + sys = QuantumSystem(0.01 * GATES[:Z], [GATES[:Y]]) + T = 50 + Δt = 0.2 + ops = IpoptOptions(print_level=1) + pi_false_ops = PiccoloOptions(verbose=false, free_time=false) + pi_true_ops = PiccoloOptions(verbose=false, free_time=true) + suffix = "_new" + timestep_symbol = :Δt + + prob1 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, piccolo_options=pi_false_ops, ipopt_options=ops) + traj1 = direct_sum(prob1.trajectory, add_suffix(prob1.trajectory, suffix), free_time=true) + + # Direct sum (shared timestep name) + @test get_suffix(traj1, suffix).timestep == timestep_symbol + @test get_suffix(traj1, suffix, remove=true).timestep == timestep_symbol + + prob2 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, ipopt_options=ops, piccolo_options=pi_true_ops) + traj2 = add_suffix(prob2.trajectory, suffix) + + # Trajectory (unique timestep name) + @test get_suffix(traj2, suffix).timestep == add_suffix(timestep_symbol, suffix) + @test get_suffix(traj2, suffix, remove=true).timestep == timestep_symbol +end + +end # module diff --git a/src/dynamics.jl b/src/dynamics.jl index 17db4bf5..c81411b0 100644 --- a/src/dynamics.jl +++ b/src/dynamics.jl @@ -9,7 +9,7 @@ export dynamics_hessian_of_lagrangian export dynamics_components using ..QuantumSystems -using ..QuantumUtils +using ..Isomorphisms using ..StructureUtils using ..Integrators @@ -39,9 +39,9 @@ function dynamics_components(integrators::Vector{<:AbstractIntegrator}) dynamics_comps = [] comp_mark = 0 for integrator ∈ integrators - integrator_comps = (comp_mark + 1):(comp_mark + dim(integrator)) + integrator_comps = (comp_mark + 1):(comp_mark + integrator.dim) push!(dynamics_comps, integrator_comps) - comp_mark += dim(integrator) + comp_mark += integrator.dim end return dynamics_comps end @@ -51,11 +51,11 @@ function dynamics( traj::NamedTrajectory ) dynamics_comps = dynamics_components(integrators) - dynamics_dim = dim(integrators) - function f(zₜ, zₜ₊₁) + dynamics_dim = sum(integrator.dim for integrator ∈ integrators) + function f(zₜ, zₜ₊₁, t) δ = Vector{eltype(zₜ)}(undef, dynamics_dim) for (integrator, integrator_comps) ∈ zip(integrators, dynamics_comps) - δ[integrator_comps] = integrator(zₜ, zₜ₊₁, traj) + δ[integrator_comps] = integrator(zₜ, zₜ₊₁, t) end return δ end @@ -69,28 +69,27 @@ function dynamics_jacobian( traj::NamedTrajectory ) dynamics_comps = dynamics_components(integrators) - dynamics_dim = dim(integrators) - free_time = traj.timestep isa Symbol - @views function ∂f(zₜ, zₜ₊₁) + dynamics_dim = sum(integrator.dim for integrator ∈ integrators) + @views function ∂f(zₜ, zₜ₊₁, t) ∂ = zeros(eltype(zₜ), dynamics_dim, 2traj.dim) for (integrator, integrator_comps) ∈ zip(integrators, dynamics_comps) if integrator isa QuantumIntegrator if integrator isa QuantumPadeIntegrator && integrator.autodiff - ∂Pᵢ(z1, z2) = ForwardDiff.jacobian( - zz -> integrator(zz[1:traj.dim], zz[traj.dim+1:end], traj), + ∂Pᵢ(z1, z2, t) = ForwardDiff.jacobian( + zz -> integrator(zz[1:traj.dim], zz[traj.dim+1:end], t), [z1; z2] ) - ∂[integrator_comps, 1:2traj.dim] = ∂Pᵢ(zₜ, zₜ₊₁) + ∂[integrator_comps, 1:2traj.dim] = ∂Pᵢ(zₜ, zₜ₊₁, t) else - if free_time - x_comps, u_comps, Δt_comps = comps(integrator, traj) + if integrator.freetime + x_comps, u_comps, Δt_comps = get_comps(integrator, traj) ∂xₜf, ∂xₜ₊₁f, ∂uₜf, ∂Δtₜf = - Integrators.jacobian(integrator, zₜ, zₜ₊₁, traj) + Integrators.jacobian(integrator, zₜ, zₜ₊₁, t) ∂[integrator_comps, Δt_comps] = ∂Δtₜf else - x_comps, u_comps = comps(integrator, traj) + x_comps, u_comps = get_comps(integrator, traj) ∂xₜf, ∂xₜ₊₁f, ∂uₜf = - Integrators.jacobian(integrator, zₜ, zₜ₊₁, traj) + Integrators.jacobian(integrator, zₜ, zₜ₊₁, t) end ∂[integrator_comps, x_comps] = ∂xₜf ∂[integrator_comps, x_comps .+ traj.dim] = ∂xₜ₊₁f @@ -103,19 +102,19 @@ function dynamics_jacobian( end end elseif integrator isa DerivativeIntegrator - if free_time - x_comps, dx_comps, Δt_comps = comps(integrator, traj) + if integrator.freetime + x_comps, dx_comps, Δt_comps = get_comps(integrator, traj) ∂xₜf, ∂xₜ₊₁f, ∂dxₜf, ∂Δtₜf = - Integrators.jacobian(integrator, zₜ, zₜ₊₁, traj) + Integrators.jacobian(integrator, zₜ, zₜ₊₁, t) else - x_comps, dx_comps = comps(integrator, traj) + x_comps, dx_comps = get_comps(integrator, traj) ∂xₜf, ∂xₜ₊₁f, ∂dxₜf = - Integrators.jacobian(integrator, zₜ, zₜ₊₁, traj) + Integrators.jacobian(integrator, zₜ, zₜ₊₁, t) end ∂[integrator_comps, x_comps] = ∂xₜf ∂[integrator_comps, x_comps .+ traj.dim] = ∂xₜ₊₁f ∂[integrator_comps, dx_comps] = ∂dxₜf - if free_time + if integrator.freetime ∂[integrator_comps, Δt_comps] = ∂Δtₜf end else @@ -209,27 +208,27 @@ function QuantumDynamics( free_time = traj.timestep isa Symbol - if free_time - @assert all([ - !isnothing(state(integrator)) && - !isnothing(controls(integrator)) - for integrator ∈ integrators - ]) - else - @assert all([ - !isnothing(state(integrator)) && - !isnothing(controls(integrator)) - for integrator ∈ integrators - ]) - end + # if free_time + # @assert all([ + # !isnothing(state(integrator)) && + # !isnothing(controls(integrator)) + # for integrator ∈ integrators + # ]) + # else + # @assert all([ + # !isnothing(state(integrator)) && + # !isnothing(controls(integrator)) + # for integrator ∈ integrators + # ]) + # end - for integrator ∈ integrators - if integrator isa QuantumIntegrator && controls(integrator) isa Tuple - drive_comps = [traj.components[s] for s ∈ integrator.drive_symb] - number_of_drives = sum(length.(drive_comps)) - @assert number_of_drives == integrator.n_drives "number of drives ($(number_of_drives)) does not match number of drive terms in Hamiltonian ($(integrator.n_drives))" - end - end + # for integrator ∈ integrators + # if integrator isa QuantumIntegrator && controls(integrator) isa Tuple + # drive_comps = [traj.components[s] for s ∈ integrator.drive_symb] + # number_of_drives = sum(length.(drive_comps)) + # @assert number_of_drives == integrator.n_drives "number of drives ($(number_of_drives)) does not match number of drive terms in Hamiltonian ($(integrator.n_drives))" + # end + # end f = dynamics(integrators, traj) @@ -245,7 +244,7 @@ function QuantumDynamics( println(" determining dynamics derivative structure...") end - dynamics_dim = dim(integrators) + dynamics_dim = sum(integrator.dim for integrator ∈ integrators) if eval_hessian ∂f_structure, ∂F_structure, μ∂²f_structure, μ∂²F_structure = @@ -276,7 +275,7 @@ function QuantumDynamics( Threads.@threads for t = 1:traj.T-1 zₜ = Z⃗[slice(t, traj.dim)] zₜ₊₁ = Z⃗[slice(t + 1, traj.dim)] - δ[slice(t, dynamics_dim)] = f(zₜ, zₜ₊₁) + δ[slice(t, dynamics_dim)] = f(zₜ, zₜ₊₁, t) end return δ end @@ -286,7 +285,7 @@ function QuantumDynamics( Threads.@threads for t = 1:traj.T-1 zₜ = Z⃗[slice(t, traj.dim)] zₜ₊₁ = Z⃗[slice(t + 1, traj.dim)] - ∂fₜ = ∂f(zₜ, zₜ₊₁) + ∂fₜ = ∂f(zₜ, zₜ₊₁, t) for (k, (i, j)) ∈ enumerate(∂f_structure) ∂s[index(t, k, ∂f_nnz)] = ∂fₜ[i, j] end @@ -311,7 +310,7 @@ function QuantumDynamics( else μ∂²F = nothing end - + return QuantumDynamics( integrators, F, diff --git a/src/embedded_operators.jl b/src/embedded_operators.jl index 57f29a2b..16b4636a 100644 --- a/src/embedded_operators.jl +++ b/src/embedded_operators.jl @@ -1,5 +1,6 @@ module EmbeddedOperators +export OperatorType export EmbeddedOperator export embed @@ -7,16 +8,19 @@ export unembed export get_subspace_indices export get_subspace_enr_indices export get_subspace_leakage_indices -export get_unitary_isomorphism_leakage_indices -export get_unitary_isomorphism_subspace_indices +export get_iso_vec_leakage_indices +export get_iso_vec_subspace_indices export get_subspace_identity using LinearAlgebra +using TestItemRunner using TrajectoryIndexingUtils -using ..QuantumUtils +using ..Isomorphisms +using ..QuantumObjectUtils using ..QuantumSystems +# using ..QuantumSystemUtils function embed(op::Matrix{ComplexF64}, subspace_indices::AbstractVector{Int}, levels::Int) @@ -29,7 +33,9 @@ function embed(op::Matrix{ComplexF64}, subspace_indices::AbstractVector{Int}, le return op_embedded end -# ==================== +# ----------------------------------------------------------------------------- # +# Embedded Operator # +# ----------------------------------------------------------------------------- # struct EmbeddedOperator operator::Matrix{ComplexF64} @@ -51,12 +57,14 @@ struct EmbeddedOperator end end +const OperatorType = Union{AbstractMatrix{<:Number}, EmbeddedOperator} + EmbeddedOperator(op::Matrix{<:Number}, subspace_indices::AbstractVector{Int}, levels::Int) = EmbeddedOperator(op, subspace_indices, [levels]) function embed(matrix::Matrix{ComplexF64}, op::EmbeddedOperator) return embed(matrix, op.subspace_indices, prod(op.subsystem_levels)) -end +end function unembed(op::EmbeddedOperator)::Matrix{ComplexF64} return op.operator[op.subspace_indices, op.subspace_indices] @@ -91,7 +99,7 @@ function Base.kron(op1::EmbeddedOperator, op2::EmbeddedOperator) return EmbeddedOperator(unembed(op1) ⊗ unembed(op2), indices, levels) end -QuantumUtils.:⊗(A::EmbeddedOperator, B::EmbeddedOperator) = kron(A, B) +QuantumObjectUtils.:⊗(A::EmbeddedOperator, B::EmbeddedOperator) = kron(A, B) function EmbeddedOperator( op::AbstractMatrix{<:Number}, @@ -151,7 +159,7 @@ function EmbeddedOperator( end function EmbeddedOperator(op::Symbol, args...; kwargs...) - @assert op ∈ keys(GATES) "Operator must be a valid gate. See QuantumCollocation.QuantumUtils.GATES dict for available gates." + @assert op ∈ keys(GATES) "Operator must be a valid gate. See QuantumCollocation.QuantumObjectUtils.GATES dict for available gates." return EmbeddedOperator(GATES[op], args...; kwargs...) end @@ -167,9 +175,9 @@ function EmbeddedOperator( return *(ops_embedded...) end -# function LinearAlgebra - -# ==================== +# ----------------------------------------------------------------------------- # +# Subspace Indices # +# ----------------------------------------------------------------------------- # basis_labels(subsystem_levels::AbstractVector{Int}; baseline=1) = kron([""], [string.(baseline:levels - 1 + baseline) for levels ∈ subsystem_levels]...) @@ -212,16 +220,16 @@ end get_subspace_leakage_indices(subspace_indices::AbstractVector{Int}, levels::Int) = setdiff(1:levels, subspace_indices) -get_subspace_leakage_indices(op::EmbeddedOperator) = +get_subspace_leakage_indices(op::EmbeddedOperator) = get_subspace_leakage_indices(op.subspace_indices, size(op)[1]) -get_unitary_isomorphism_subspace_indices(op::EmbeddedOperator) = - get_unitary_isomorphism_subspace_indices(op.subspace_indices, op.subsystem_levels) +get_iso_vec_subspace_indices(op::EmbeddedOperator) = + get_iso_vec_subspace_indices(op.subspace_indices, op.subsystem_levels) -get_unitary_isomorphism_leakage_indices(op::EmbeddedOperator) = - get_unitary_isomorphism_leakage_indices(op.subspace_indices, op.subsystem_levels) +get_iso_vec_leakage_indices(op::EmbeddedOperator) = + get_iso_vec_leakage_indices(op.subspace_indices, op.subsystem_levels) -function get_unitary_isomorphism_subspace_indices( +function get_iso_vec_subspace_indices( subspace_indices::AbstractVector{Int}, subsystem_levels::AbstractVector{Int} ) @@ -238,7 +246,7 @@ function get_unitary_isomorphism_subspace_indices( return iso_subspace_indices end -function get_unitary_isomorphism_leakage_indices( +function get_iso_vec_leakage_indices( subspace_indices::AbstractVector{Int}, subsystem_levels::AbstractVector{Int} ) @@ -264,5 +272,120 @@ function get_subspace_identity(op::EmbeddedOperator) ) end +# =========================================================================== # + +@testitem "Basis labels" begin + levels = [3, 3] + labels = ["11", "12", "13", "21", "22", "23", "31", "32", "33"] + @test EmbeddedOperators.basis_labels(levels, baseline=1) == labels + + labels = ["1", "2", "3"] + @test EmbeddedOperators.basis_labels(3, baseline=1) == labels + @test EmbeddedOperators.basis_labels([3], baseline=1) == labels + + labels = ["0", "1", "2"] + @test EmbeddedOperators.basis_labels(3, baseline=0) == labels + @test EmbeddedOperators.basis_labels([3], baseline=0) == labels + + levels = [2, 2] + labels = ["00", "01", "10", "11"] + @test EmbeddedOperators.basis_labels(levels, baseline=0) == labels +end + +@testitem "Subspace Indices" begin + @test get_subspace_indices([1, 2], 3) == [1, 2] + # 2 * 2 = 4 elements + @test get_subspace_indices([1:2, 1:2], [3, 3]) == [1, 2, 4, 5] + # 1 * 1 = 1 element + @test get_subspace_indices([[2], [2]], [3, 3]) == [5] + # 1 * 2 = 2 elements + @test get_subspace_indices([[2], 1:2], [3, 3]) == [4, 5] +end + +@testitem "Subspace ENR Indices" begin + # 00, 01, 02x, 10, 11x, 12x, 20x, 21x, 22x + @test get_subspace_enr_indices(1, [3, 3]) == [1, 2, 4] + # 00, 01, 02, 10, 11, 12x, 20, 21x, 22x + @test get_subspace_enr_indices(2, [3, 3]) == [1, 2, 3, 4, 5, 7] + # 00, 01, 02, 10, 11, 12, 20, 21, 22x + @test get_subspace_enr_indices(3, [3, 3]) == [1, 2, 3, 4, 5, 6, 7, 8] + # 00, 01, 02, 10, 11, 12, 20, 21, 22 + @test get_subspace_enr_indices(4, [3, 3]) == [1, 2, 3, 4, 5, 6, 7, 8, 9] +end + +@testitem "Subspace Leakage Indices" begin + # TODO: Implement tests +end + +@testitem "Embedded operator" begin + # Embed X + op = Matrix{ComplexF64}([0 1; 1 0]) + embedded_op = Matrix{ComplexF64}([0 1 0 0; 1 0 0 0; 0 0 0 0; 0 0 0 0]) + @test embed(op, 1:2, 4) == embedded_op + embedded_op_struct = EmbeddedOperator(op, 1:2, 4) + @test embedded_op_struct.operator == embedded_op + @test embedded_op_struct.subspace_indices == 1:2 + @test embedded_op_struct.subsystem_levels == [4] + + # Properties + @test size(embedded_op_struct) == size(embedded_op) + @test size(embedded_op_struct, 1) == size(embedded_op, 1) + + # X^2 = I + x2 = (embedded_op_struct * embedded_op_struct).operator + id = get_subspace_identity(embedded_op_struct) + @test x2 == id + + # Embed X twice + op2 = op ⊗ op + embedded_op2 = [ + 0 0 0 0 1 0 0 0 0; + 0 0 0 1 0 0 0 0 0; + 0 0 0 0 0 0 0 0 0; + 0 1 0 0 0 0 0 0 0; + 1 0 0 0 0 0 0 0 0; + 0 0 0 0 0 0 0 0 0; + 0 0 0 0 0 0 0 0 0; + 0 0 0 0 0 0 0 0 0; + 0 0 0 0 0 0 0 0 0 + ] + subspace_indices = get_subspace_indices([1:2, 1:2], [3, 3]) + @test embed(op2, subspace_indices, 9) == embedded_op2 + embedded_op2_struct = EmbeddedOperator(op2, subspace_indices, [3, 3]) + @test embedded_op2_struct.operator == embedded_op2 + @test embedded_op2_struct.subspace_indices == subspace_indices + @test embedded_op2_struct.subsystem_levels == [3, 3] +end + +@testitem "Embedded operator from system" begin + CZ = GATES[:CZ] + a = annihilate(3) + σ_x = a + a' + σ_y = -1im*(a - a') + system = QuantumSystem([σ_x ⊗ σ_x, σ_y ⊗ σ_y]) + + op_explicit_qubit = EmbeddedOperator( + CZ, + system, + subspace=get_subspace_indices([1:2, 1:2], [3, 3]) + ) + op_implicit_qubit = EmbeddedOperator(CZ, system) + # This does not work (implicit puts indicies in 1:4) + @test op_implicit_qubit.operator != op_explicit_qubit.operator + # But the ops are the same + @test unembed(op_explicit_qubit) == unembed(op_implicit_qubit) + @test unembed(op_implicit_qubit) == CZ +end + +@testitem "Embedded operator from composite system" begin + @test_skip nothing +end + +@testitem "Embedded operator kron" begin + Z = GATES[:Z] + Ẑ = EmbeddedOperator(Z, 1:2, [4]) + @test unembed(Ẑ ⊗ Ẑ) == Z ⊗ Z +end + end diff --git a/src/integrators/_integrators.jl b/src/integrators/_integrators.jl index 590086ff..d90f1fc8 100644 --- a/src/integrators/_integrators.jl +++ b/src/integrators/_integrators.jl @@ -12,15 +12,11 @@ export QuantumStateExponentialIntegrator export DerivativeIntegrator -export state -export controls -export timestep -export comps -export dim - export jacobian export hessian_of_the_lagrangian +export get_comps + export nth_order_pade export fourth_order_pade export sixth_order_pade @@ -28,7 +24,10 @@ export eighth_order_pade export tenth_order_pade using ..QuantumSystems -using ..QuantumUtils +using ..Isomorphisms +using ..QuantumObjectUtils +using ..Losses +using ..QuantumSystemUtils using NamedTrajectories using TrajectoryIndexingUtils @@ -57,9 +56,6 @@ function comps(P::AbstractIntegrator, traj::NamedTrajectory) end end -dim(integrator::AbstractIntegrator) = integrator.dim -dim(integrators::AbstractVector{<:AbstractIntegrator}) = sum(dim, integrators) - include("_integrator_utils.jl") diff --git a/src/integrators/derivative_integrator.jl b/src/integrators/derivative_integrator.jl index 5e6d41b1..e8a5986a 100644 --- a/src/integrators/derivative_integrator.jl +++ b/src/integrators/derivative_integrator.jl @@ -3,10 +3,14 @@ ### Derivative Integrator ### -struct DerivativeIntegrator <: AbstractIntegrator - variable::Symbol - derivative::Symbol +mutable struct DerivativeIntegrator <: AbstractIntegrator + variable_components::Vector{Int} + derivative_components::Vector{Int} + freetime::Bool + timestep::Union{Float64, Int} # timestep of index in z + zdim::Int dim::Int + autodiff::Bool end function DerivativeIntegrator( @@ -14,7 +18,35 @@ function DerivativeIntegrator( derivative::Symbol, traj::NamedTrajectory ) - return DerivativeIntegrator(variable, derivative, traj.dims[variable]) + freetime = traj.timestep isa Symbol + if freetime + timestep = traj.components[traj.timestep][1] + else + timestep = traj.timestep + end + return DerivativeIntegrator( + traj.components[variable], + traj.components[derivative], + freetime, + timestep, + traj.dim, + traj.dims[variable], + false + ) +end + +function (integrator::DerivativeIntegrator)( + traj::NamedTrajectory; + variable::Union{Symbol, Nothing}=nothing, + derivative::Union{Symbol, Nothing}=nothing, +) + @assert !isnothing(variable) "variable must be provided" + @assert !isnothing(derivative) "derivative must be provided" + return DerivativeIntegrator( + variable, + derivative, + traj + ) end state(integrator::DerivativeIntegrator) = integrator.variable @@ -23,15 +55,15 @@ controls(integrator::DerivativeIntegrator) = integrator.derivative @views function (D::DerivativeIntegrator)( zₜ::AbstractVector, zₜ₊₁::AbstractVector, - traj::NamedTrajectory + t::Int ) - xₜ = zₜ[traj.components[D.variable]] - xₜ₊₁ = zₜ₊₁[traj.components[D.variable]] - dxₜ = zₜ[traj.components[D.derivative]] - if traj.timestep isa Symbol - Δtₜ = zₜ[traj.components[traj.timestep]][1] + xₜ = zₜ[D.variable_components] + xₜ₊₁ = zₜ₊₁[D.variable_components] + dxₜ = zₜ[D.derivative_components] + if D.freetime + Δtₜ = zₜ[D.timestep] else - Δtₜ = traj.timestep + Δtₜ = D.timestep end return xₜ₊₁ - xₜ - Δtₜ * dxₜ end @@ -40,13 +72,13 @@ end D::DerivativeIntegrator, zₜ::AbstractVector, zₜ₊₁::AbstractVector, - traj::NamedTrajectory + t::Int ) - dxₜ = zₜ[traj.components[D.derivative]] - if traj.timestep isa Symbol - Δtₜ = zₜ[traj.components[traj.timestep]][1] + dxₜ = zₜ[D.derivative_components] + if D.freetime + Δtₜ = zₜ[D.timestep] else - Δtₜ = traj.timestep + Δtₜ = D.timestep end ∂xₜD = sparse(-1.0I(D.dim)) ∂xₜ₊₁D = sparse(1.0I(D.dim)) @@ -54,3 +86,11 @@ end ∂ΔtₜD = -dxₜ return ∂xₜD, ∂xₜ₊₁D, ∂dxₜD, ∂ΔtₜD end + +function get_comps(D::DerivativeIntegrator, traj::NamedTrajectory) + if D.freetime + return D.variable_components, D.derivative_components, traj.components[traj.timestep] + else + return D.variable_components, D.derivative_components + end +end diff --git a/src/integrators/exponential_integrators.jl b/src/integrators/exponential_integrators.jl index 68386db3..40cf1f0e 100644 --- a/src/integrators/exponential_integrators.jl +++ b/src/integrators/exponential_integrators.jl @@ -4,73 +4,128 @@ This file includes expoential integrators for states and unitaries using ExponentialAction +# ----------------------------------------------------------------------------- # +# Quantum Exponential Integrators # +# ----------------------------------------------------------------------------- # abstract type QuantumExponentialIntegrator <: QuantumIntegrator end +# ----------------------------------------------------------------------------- # +# Unitary Exponential Integrator # +# ----------------------------------------------------------------------------- # + struct UnitaryExponentialIntegrator <: QuantumExponentialIntegrator - G_drift::SparseMatrixCSC{Float64, Int} - G_drives::Vector{SparseMatrixCSC{Float64, Int}} - unitary_name::Symbol - drive_names::Union{Symbol, Tuple{Vararg{Symbol}}} + unitary_components::Vector{Int} + drive_components::Vector{Int} + timestep::Union{Real, Int} + freetime::Bool n_drives::Int ketdim::Int dim::Int + zdim::Int + autodiff::Bool G::Function function UnitaryExponentialIntegrator( sys::AbstractQuantumSystem, unitary_name::Symbol, - drive_names::Union{Symbol, Tuple{Vararg{Symbol}}} + drive_name::Union{Symbol, Tuple{Vararg{Symbol}}}, + traj::NamedTrajectory; + G::Function=a -> G_bilinear(a, sys.G_drift, sys.G_drives), + autodiff::Bool=false ) - n_drives = length(sys.H_drives) ketdim = size(sys.H_drift, 1) dim = 2ketdim^2 + unitary_components = traj.components[unitary_name] + + if drive_name isa Tuple + drive_components = vcat((traj.components[s] for s ∈ drive_name)...) + else + drive_components = traj.components[drive_name] + end + + n_drives = length(drive_components) + + @assert all(diff(drive_components) .== 1) "controls must be in order" + + freetime = traj.timestep isa Symbol + + if freetime + timestep = traj.components[traj.timestep][1] + else + timestep = traj.timestep + end + return new( - sys.G_drift, - sys.G_drives, - unitary_name, - drive_names, + unitary_components, + drive_components, + timestep, + freetime, n_drives, ketdim, dim, - G_bilinear + traj.dim, + autodiff, + G ) end end -state(integrator::UnitaryExponentialIntegrator) = integrator.unitary_name -controls(integrator::UnitaryExponentialIntegrator) = integrator.drive_names +function (integrator::UnitaryExponentialIntegrator)( + sys::AbstractQuantumSystem, + traj::NamedTrajectory; + unitary_name::Union{Nothing, Symbol}=nothing, + drive_name::Union{Nothing, Symbol, Tuple{Vararg{Symbol}}}=nothing, + G::Function=integrator.G, + autodiff::Bool=integrator.autodiff +) + @assert !isnothing(unitary_name) "unitary_name must be provided" + @assert !isnothing(drive_name) "drive_name must be provided" + return UnitaryExponentialIntegrator( + sys, + unitary_name, + drive_name, + traj; + G=G, + autodiff=autodiff + ) +end + +function get_comps(P::UnitaryExponentialIntegrator, traj::NamedTrajectory) + if P.freetime + return P.unitary_components, P.drive_components, traj.components[traj.timestep] + else + return P.unitary_components, P.drive_components + end +end + +# ------------------------------ Integrator --------------------------------- # @views function (ℰ::UnitaryExponentialIntegrator)( zₜ::AbstractVector, zₜ₊₁::AbstractVector, - traj::NamedTrajectory + t::Int ) - Ũ⃗ₜ₊₁ = zₜ₊₁[traj.components[ℰ.unitary_name]] - Ũ⃗ₜ = zₜ[traj.components[ℰ.unitary_name]] - - if traj.timestep isa Symbol - Δtₜ = zₜ[traj.components[traj.timestep]][1] - else - Δtₜ = traj.timestep - end + Ũ⃗ₜ₊₁ = zₜ₊₁[ℰ.unitary_components] + Ũ⃗ₜ = zₜ[ℰ.unitary_components] + aₜ = zₜ[ℰ.drive_components] - if ℰ.drive_names isa Tuple - aₜ = vcat([zₜ[traj.components[name]] for name ∈ ℰ.drive_names]...) + if ℰ.freetime + Δtₜ = zₜ[ℰ.timestep] else - aₜ = zₜ[traj.components[ℰ.drive_names]] + Δtₜ = ℰ.timestep end - Gₜ = ℰ.G(aₜ, ℰ.G_drift, ℰ.G_drives) + Gₜ = ℰ.G(aₜ) return Ũ⃗ₜ₊₁ - expv(Δtₜ, I(ℰ.ketdim) ⊗ Gₜ, Ũ⃗ₜ) end function hermitian_exp(G::AbstractMatrix) - Ĥ = Hermitian(Matrix(QuantumSystems.H(G))) + Ĥ = Hermitian(Matrix(Isomorphisms.H(G))) λ, V = eigen(Ĥ) - expG = QuantumSystems.iso(sparse(V * Diagonal(exp.(-im * λ)) * V')) + expG = Isomorphisms.iso(sparse(V * Diagonal(exp.(-im * λ)) * V')) droptol!(expG, 1e-12) return expG end @@ -79,28 +134,21 @@ end ℰ::UnitaryExponentialIntegrator, zₜ::AbstractVector, zₜ₊₁::AbstractVector, - traj::NamedTrajectory + t::Int ) - free_time = traj.timestep isa Symbol + # get the state and control vectors + Ũ⃗ₜ = zₜ[ℰ.unitary_components] + aₜ = zₜ[ℰ.drive_components] - Ũ⃗ₜ = zₜ[traj.components[ℰ.unitary_name]] - - Δtₜ = free_time ? zₜ[traj.components[traj.timestep]][1] : traj.timestep - - if ℰ.drive_names isa Tuple - inds = [traj.components[s] for s in ℰ.drive_names] - inds = vcat(collect.(inds)...) + # obtain the timestep + if ℰ.freetime + Δtₜ = zₜ[ℰ.timestep] else - inds = traj.components[ℰ.drive_names] + Δtₜ = ℰ.timestep end - for i = 1:length(inds) - 1 - @assert inds[i] + 1 == inds[i + 1] "Controls must be in order" - end - - aₜ = zₜ[inds] - - Gₜ = ℰ.G(aₜ, ℰ.G_drift, ℰ.G_drives) + # compute the generator + Gₜ = ℰ.G(aₜ) Id = I(ℰ.ketdim) @@ -110,11 +158,11 @@ end ∂Ũ⃗ₜℰ = -expĜₜ ∂aₜℰ = -ForwardDiff.jacobian( - a -> expv(Δtₜ, Id ⊗ ℰ.G(a, ℰ.G_drift, ℰ.G_drives), Ũ⃗ₜ), + a -> expv(Δtₜ, Id ⊗ ℰ.G(a), Ũ⃗ₜ), aₜ ) - if free_time + if ℰ.freetime ∂Δtₜℰ = -(Id ⊗ Gₜ) * expĜₜ * Ũ⃗ₜ return ∂Ũ⃗ₜℰ, ∂Ũ⃗ₜ₊₁ℰ, ∂aₜℰ, ∂Δtₜℰ else @@ -123,61 +171,110 @@ end end struct QuantumStateExponentialIntegrator <: QuantumExponentialIntegrator - G_drift::SparseMatrixCSC{Float64, Int} - G_drives::Vector{SparseMatrixCSC{Float64, Int}} - state_name::Symbol - drive_names::Union{Symbol, Tuple{Vararg{Symbol}}} + state_components::Vector{Int} + drive_components::Vector{Int} + timestep::Union{Real, Int} + freetime::Bool n_drives::Int ketdim::Int dim::Int + zdim::Int + autodiff::Bool G::Function function QuantumStateExponentialIntegrator( sys::AbstractQuantumSystem, state_name::Symbol, - drive_names::Union{Symbol, Tuple{Vararg{Symbol}}} + drive_name::Union{Symbol, Tuple{Vararg{Symbol}}}, + traj::NamedTrajectory; + G::Function=a -> G_bilinear(a, sys.G_drift, sys.G_drives), + autodiff::Bool=false ) - n_drives = length(sys.H_drives) ketdim = size(sys.H_drift, 1) dim = 2ketdim + state_components = traj.components[state_name] + + if drive_name isa Tuple + drive_components = vcat((traj.components[s] for s ∈ drive_name)...) + else + drive_components = traj.components[drive_name] + end + + n_drives = length(drive_components) + + @assert all(diff(drive_components) .== 1) "controls must be in order" + + freetime = traj.timestep isa Symbol + + if freetime + timestep = traj.components[traj.timestep][1] + else + timestep = traj.timestep + end + return new( - sys.G_drift, - sys.G_drives, - state_name, - drive_names, + state_components, + drive_components, + timestep, + freetime, n_drives, ketdim, dim, - G_bilinear + traj.dim, + autodiff, + G ) end end -state(integrator::QuantumStateExponentialIntegrator) = integrator.state_name -controls(integrator::QuantumStateExponentialIntegrator) = integrator.drive_names +function get_comps(P::QuantumStateExponentialIntegrator, traj::NamedTrajectory) + if P.freetime + return P.state_components, P.drive_components, traj.components[traj.timestep] + else + return P.state_components, P.drive_components + end +end + +function (integrator::QuantumStateExponentialIntegrator)( + sys::AbstractQuantumSystem, + traj::NamedTrajectory; + state_name::Union{Nothing, Symbol}=nothing, + drive_name::Union{Nothing, Symbol, Tuple{Vararg{Symbol}}}=nothing, + G::Function=integrator.G, + autodiff::Bool=integrator.autodiff +) + @assert !isnothing(state_name) "state_name must be provided" + @assert !isnothing(drive_name) "drive_name must be provided" + return QuantumStateExponentialIntegrator( + sys, + state_name, + drive_name, + traj; + G=G, + autodiff=autodiff + ) +end + +# ------------------------------ Integrator --------------------------------- # @views function (ℰ::QuantumStateExponentialIntegrator)( zₜ::AbstractVector, zₜ₊₁::AbstractVector, - traj::NamedTrajectory + t::Int ) - ψ̃ₜ₊₁ = zₜ₊₁[traj.components[ℰ.state_name]] - ψ̃ₜ = zₜ[traj.components[ℰ.state_name]] + ψ̃ₜ₊₁ = zₜ₊₁[ℰ.state_components] + ψ̃ₜ = zₜ[ℰ.state_components] + aₜ = zₜ[ℰ.drive_components] - if traj.timestep isa Symbol - Δtₜ = zₜ[traj.components[traj.timestep]][1] + if ℰ.freetime + Δtₜ = zₜ[ℰ.timestep] else - Δtₜ = traj.timestep + Δtₜ = ℰ.timestep end - if ℰ.drive_names isa Tuple - aₜ = vcat([zₜ[traj.components[name]] for name ∈ ℰ.drive_names]...) - else - aₜ = zₜ[traj.components[ℰ.drive_names]] - end + Gₜ = ℰ.G(aₜ) - Gₜ = ℰ.G(aₜ, ℰ.G_drift, ℰ.G_drives) return ψ̃ₜ₊₁ - expv(Δtₜ, Gₜ, ψ̃ₜ) end @@ -186,28 +283,21 @@ end ℰ::QuantumStateExponentialIntegrator, zₜ::AbstractVector, zₜ₊₁::AbstractVector, - traj::NamedTrajectory + t::Int ) - free_time = traj.timestep isa Symbol - - ψ̃ₜ = zₜ[traj.components[ℰ.state_name]] + # get the state and control vectors + ψ̃ₜ = zₜ[ℰ.state_components] + aₜ = zₜ[ℰ.drive_components] - Δtₜ = free_time ? zₜ[traj.components[traj.timestep]][1] : traj.timestep - - if ℰ.drive_names isa Tuple - inds = [traj.components[s] for s in ℰ.drive_names] - inds = vcat(collect.(inds)...) + # obtain the timestep + if ℰ.freetime + Δtₜ = zₜ[ℰ.timestep] else - inds = traj.components[ℰ.drive_names] - end - - for i = 1:length(inds) - 1 - @assert inds[i] + 1 == inds[i + 1] "Controls must be in order" + Δtₜ = ℰ.timestep end - aₜ = zₜ[inds] - - Gₜ = ℰ.G(aₜ, ℰ.G_drift, ℰ.G_drives) + # compute the generator + Gₜ = ℰ.G(aₜ) expGₜ = hermitian_exp(Δtₜ * Gₜ) @@ -215,11 +305,11 @@ end ∂ψ̃ₜℰ = -expGₜ ∂aₜℰ = -ForwardDiff.jacobian( - a -> expv(Δtₜ, ℰ.G(a, ℰ.G_drift, ℰ.G_drives), ψ̃ₜ), + a -> expv(Δtₜ, ℰ.G(a), ψ̃ₜ), aₜ ) - if free_time + if ℰ.freetime ∂Δtₜℰ = -Gₜ * expGₜ * ψ̃ₜ return ∂ψ̃ₜℰ, ∂ψ̃ₜ₊₁ℰ, ∂aₜℰ, ∂Δtₜℰ else @@ -260,13 +350,13 @@ end goal=(Ũ⃗ = Ũ⃗_goal,) ) - ℰ = UnitaryExponentialIntegrator(system, :Ũ⃗, :a) + ℰ = UnitaryExponentialIntegrator(system, :Ũ⃗, :a, Z) - ∂Ũ⃗ₜℰ, ∂Ũ⃗ₜ₊₁ℰ, ∂aₜℰ, ∂Δtₜℰ = jacobian(ℰ, Z[1].data, Z[2].data, Z) + ∂Ũ⃗ₜℰ, ∂Ũ⃗ₜ₊₁ℰ, ∂aₜℰ, ∂Δtₜℰ = jacobian(ℰ, Z[1].data, Z[2].data, 1) ∂ℰ_forwarddiff = ForwardDiff.jacobian( - zz -> ℰ(zz[1:Z.dim], zz[Z.dim+1:end], Z), + zz -> ℰ(zz[1:Z.dim], zz[Z.dim+1:end], 1), [Z[1].data; Z[2].data] ) @@ -290,8 +380,8 @@ end U_init = GATES[:I] U_goal = GATES[:X] - ψ̃_init = ket_to_iso(quantum_state("g", [2])) - ψ̃_goal = ket_to_iso(quantum_state("e", [2])) + ψ̃_init = ket_to_iso(ket_from_string("g", [2])) + ψ̃_goal = ket_to_iso(ket_from_string("e", [2])) dt = 0.1 @@ -307,12 +397,11 @@ end goal=(ψ̃ = ψ̃_goal,) ) - ℰ = QuantumStateExponentialIntegrator(system, :ψ̃, :a) - - ∂ψ̃ₜℰ, ∂ψ̃ₜ₊₁ℰ, ∂aₜℰ, ∂Δtₜℰ = jacobian(ℰ, Z[1].data, Z[2].data, Z) + ℰ = QuantumStateExponentialIntegrator(system, :ψ̃, :a, Z) + ∂ψ̃ₜℰ, ∂ψ̃ₜ₊₁ℰ, ∂aₜℰ, ∂Δtₜℰ = jacobian(ℰ, Z[1].data, Z[2].data, 1) ∂ℰ_forwarddiff = ForwardDiff.jacobian( - zz -> ℰ(zz[1:Z.dim], zz[Z.dim+1:end], Z), + zz -> ℰ(zz[1:Z.dim], zz[Z.dim+1:end], 1), [Z[1].data; Z[2].data] ) diff --git a/src/integrators/pade_integrators.jl b/src/integrators/pade_integrators.jl index 6eab2f03..549bc70b 100644 --- a/src/integrators/pade_integrators.jl +++ b/src/integrators/pade_integrators.jl @@ -15,6 +15,11 @@ fourth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 4) sixth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 6) eighth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 8) tenth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 10) +twelth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 12) +fourteenth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 14) +sixteenth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 16) +eighteenth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 18) +twentieth_order_pade(Gₜ::Matrix) = nth_order_pade(Gₜ, 20) function compute_powers(G::AbstractMatrix{T}, order::Int) where T <: Number powers = Array{typeof(G)}(undef, order) @@ -28,38 +33,200 @@ end # key is the order of the integrator # and the value are the Pade coefficients # for each term -const PADE_COEFFICIENTS = Dict{Int,Vector{Float64}}( +const PADE_COEFFICIENTS = OrderedDict{Int,Vector{Float64}}( 4 => [1/2, 1/12], 6 => [1/2, 1/10, 1/120], 8 => [1/2, 3/28, 1/84, 1/1680], - 10 => [1/2, 1/9, 1/72, 1/1008, 1/30240] + 10 => [1/2, 1/9, 1/72, 1/1008, 1/30240], + 12 => [1/2, 5/44, 1/66, 1/792, 1/15840, 1/665280], + 14 => [1/2, 3/26, 5/312, 5/3432, 1/11440, 1/308880, 1/17297280], + 16 => [1/2, 7/60, 1/60, 1/624, 1/9360, 1/205920, 1/7207200, 1/518918400], + 18 => [1/2, 2/17, 7/408, 7/4080, 1/8160, 1/159120, 1/4455360, 1/196035840, 1/17643225600], + 20 => [1/2, 9/76, 1/57, 7/3876, 7/51680, 7/930240, 1/3255840, 1/112869120, 1/6094932480, 1/670442572800] ) +function pade_operator( + Id::AbstractMatrix, + G_powers::Vector{<:AbstractMatrix}, + coeffs::Vector{<:Real} +) + return Id + sum(coeffs .* G_powers) +end + +function forward_pade_coefficients(Δt::Real, pade_order::Int; timestep_derivative=false) + n = pade_order ÷ 2 + if !timestep_derivative + return PADE_COEFFICIENTS[2n] .* (Δt .^ (1:n)) + else + return PADE_COEFFICIENTS[2n] .* (Δt .^ (0:n-1)) .* (1:n) + end +end + +function backward_pade_coefficients(Δt::Real, pade_order::Int; timestep_derivative=false) + n = pade_order ÷ 2 + if !timestep_derivative + return PADE_COEFFICIENTS[2n] .* ((-Δt) .^ (1:n)) + else + return PADE_COEFFICIENTS[2n] .* ((-1) .^ (1:n)) .* (Δt .^ (0:n-1)) .* (1:n) + end +end + +function pade_coefficients(Δt::Real, pade_order::Int; timestep_derivative=false) + F_coeffs = forward_pade_coefficients(Δt, pade_order; + timestep_derivative=timestep_derivative + ) + B_coeffs = backward_pade_coefficients(Δt, pade_order; + timestep_derivative=timestep_derivative + ) + return F_coeffs, B_coeffs +end + +function backward_operator( + G_powers::Vector{<:AbstractMatrix}, + Id::AbstractMatrix, + Δt::Real; + timestep_derivative=false +) + pade_order = 2 * length(G_powers) + coeffs = backward_pade_coefficients(Δt, pade_order; timestep_derivative=timestep_derivative) + return pade_operator(Id, G_powers, coeffs) +end + +backward_operator(G::AbstractMatrix, pade_order::Int, args...; kwargs...) = + backward_operator(compute_powers(G, pade_order ÷ 2), args...; kwargs...) + +function forward_operator( + G_powers::Vector{<:AbstractMatrix}, + Id::AbstractMatrix, + Δt::Real; + timestep_derivative=false +) + pade_order = 2 * length(G_powers) + coeffs = forward_pade_coefficients(Δt, pade_order; timestep_derivative=timestep_derivative) + return pade_operator(Id, G_powers, coeffs) +end + +forward_operator(G::AbstractMatrix, pade_order::Int, args...; kwargs...) = + forward_operator(compute_powers(G, pade_order ÷ 2), args...; kwargs...) + +function pade_operators( + G_powers::Vector{<:AbstractMatrix}, + Id::AbstractMatrix, + Δt::Real; + kwargs... +) + F = forward_operator(G_powers, Id, Δt; kwargs...) + B = backward_operator(G_powers, Id, Δt; kwargs...) + return F, B +end + +function pade_operators( + G_powers::Vector{<:SparseMatrixCSC}, + Id::SparseMatrixCSC, + Δt::Real; + kwargs... +) + F = forward_operator(G_powers, Id, Δt; kwargs...) + B = backward_operator(G_powers, Id, Δt; kwargs...) + droptol!(F, 1e-12) + droptol!(B, 1e-12) + return F, B +end + +pade_operators(G::AbstractMatrix, pade_order::Int, args...; kwargs...) = + pade_operators(compute_powers(G, pade_order ÷ 2), args...; kwargs...) + +@views function ∂aʲF( + P::QuantumIntegrator, + G_powers::Vector{<:AbstractMatrix}, + Δt::Real, + ∂G_∂aʲ::AbstractMatrix +) + F_coeffs = forward_pade_coefficients(Δt, P.order) + ∂F_∂aʲ = zeros(size(G_powers[1])) + n = length(G_powers) + for p = 1:n + if p == 1 + ∂F_∂aʲ += F_coeffs[p] * ∂G_∂aʲ + else + for k = 1:p + if k == 1 + ∂F_∂aʲ += F_coeffs[p] * ∂G_∂aʲ * G_powers[p-1] + elseif k == p + ∂F_∂aʲ += F_coeffs[p] * G_powers[p-1] * ∂G_∂aʲ + else + ∂F_∂aʲ += F_coeffs[p] * G_powers[k-1] * ∂G_∂aʲ * G_powers[p-k] + end + end + end + end + return ∂F_∂aʲ +end + +@views function ∂aʲB( + P::QuantumIntegrator, + G_powers::Vector{<:AbstractMatrix}, + Δt::Real, + ∂G_∂aʲ::AbstractMatrix +) + B_coeffs = backward_pade_coefficients(Δt, P.order) + ∂B_∂aʲ = zeros(size(G_powers[1])) + for p = 1:(P.order ÷ 2) + if p == 1 + ∂B_∂aʲ += B_coeffs[p] * ∂G_∂aʲ + else + for k = 1:p + if k == 1 + ∂B_∂aʲ += B_coeffs[p] * ∂G_∂aʲ * G_powers[p-1] + elseif k == p + ∂B_∂aʲ += B_coeffs[p] * G_powers[p-1] * ∂G_∂aʲ + else + ∂B_∂aʲ += B_coeffs[p] * G_powers[k-1] * ∂G_∂aʲ * G_powers[p-k] + end + end + end + end + return ∂B_∂aʲ +end + + + +# ---------------------------------------------------------------- +# Quantum Pade Integrator +# ---------------------------------------------------------------- + + abstract type QuantumPadeIntegrator <: QuantumIntegrator end + + +# ---------------------------------------------------------------- +# Unitary Pade Integrator +# ---------------------------------------------------------------- + + """ """ struct UnitaryPadeIntegrator <: QuantumPadeIntegrator - I_2N::SparseMatrixCSC{Float64, Int} - G_drift::Matrix{Float64} - G_drives::Vector{Matrix{Float64}} - G_drive_anticomms::Union{Nothing, Symmetric} - G_drift_anticomms::Union{Nothing, Vector{Matrix{Float64}}} - unitary_symb::Union{Symbol, Nothing} - drive_symb::Union{Symbol, Tuple{Vararg{Symbol}}, Nothing} + unitary_components::Vector{Int} + drive_components::Vector{Int} + timestep::Union{Real, Int} # either the timestep or the index of the timestep + freetime::Bool n_drives::Int - N::Int + ketdim::Int dim::Int + zdim::Int order::Int autodiff::Bool G::Function + ∂G::Function """ UnitaryPadeIntegrator( sys::AbstractQuantumSystem, - unitary_symb::Symbol, - drive_symb::Union{Symbol,Tuple{Vararg{Symbol}}}; + unitary_name::Symbol, + drive_name::Union{Symbol,Tuple{Vararg{Symbol}}}; order::Int=4, autodiff::Bool=order != 4 ) where R <: Real @@ -85,78 +252,250 @@ struct UnitaryPadeIntegrator <: QuantumPadeIntegrator # Arguments - `sys::AbstractQuantumSystem`: the quantum system - - `unitary_symb::Union{Symbol,Nothing}=nothing`: the symbol for the unitary - - `drive_symb::Union{Symbol,Tuple{Vararg{Symbol}},Nothing}=nothing`: the symbol(s) for the drives + - `unitary_name::Union{Symbol,Nothing}=nothing`: the nameol for the unitary + - `drive_name::Union{Symbol,Tuple{Vararg{Symbol}},Nothing}=nothing`: the nameol(s) for the drives - `order::Int=4`: the order of the Pade approximation. Must be in `[4, 6, 8, 10]`. If order is not `4` and `autodiff` is `false`, then the integrator will use the hand-coded fourth order derivatives. - `autodiff::Bool=order != 4`: whether to use automatic differentiation to compute the jacobian and hessian of the lagrangian """ function UnitaryPadeIntegrator( sys::AbstractQuantumSystem, - unitary_symb::Union{Symbol,Nothing}=nothing, - drive_symb::Union{Symbol,Tuple{Vararg{Symbol}},Nothing}=nothing; + unitary_name::Symbol, + drive_name::Union{Symbol,Tuple{Vararg{Symbol}}}, + traj::NamedTrajectory; order::Int=4, - autodiff::Bool=order != 4, - G::Function=G_bilinear, + G::Function=a -> G_bilinear(a, sys.G_drift, sys.G_drives), + ∂G::Function=a -> sys.G_drives, + calculate_pade_operators_structure::Bool=true, + autodiff::Bool=false ) - @assert order ∈ [4, 6, 8, 10] "order must be in [4, 6, 8, 10]" - @assert !isnothing(unitary_symb) "must specify unitary symbol" - @assert !isnothing(drive_symb) "must specify drive symbol" + @assert order ∈ keys(PADE_COEFFICIENTS) "order ∉ $(keys(PADE_COEFFICIENTS))" - n_drives = length(sys.H_drives) - N = size(sys.H_drift, 1) - dim = 2N^2 + ketdim = size(sys.H_drift, 1) + dim = 2ketdim^2 + + I_2N = sparse(I(2ketdim)) + + unitary_components = traj.components[unitary_name] + + if drive_name isa Tuple + drive_components = vcat((traj.components[s] for s ∈ drive_name)...) + else + drive_components = traj.components[drive_name] + end - I_2N = sparse(I(2N)) + n_drives = length(drive_components) - G_drift = sys.G_drift - G_drives = sys.G_drives + @assert all(diff(drive_components) .== 1) "controls must be in order" - drive_anticomms, drift_anticomms = - order == 4 ? build_anticomms(G_drift, G_drives, n_drives) : (nothing, nothing) + freetime = traj.timestep isa Symbol + + if freetime + timestep = traj.components[traj.timestep][1] + else + timestep = traj.timestep + end return new( - I_2N, - G_drift, - G_drives, - drive_anticomms, - drift_anticomms, - unitary_symb, - drive_symb, + unitary_components, + drive_components, + timestep, + freetime, n_drives, - N, + ketdim, dim, + traj.dim, order, autodiff, G, + ∂G ) end end -state(P::UnitaryPadeIntegrator) = P.unitary_symb -controls(P::UnitaryPadeIntegrator) = P.drive_symb +function get_comps(P::UnitaryPadeIntegrator, traj::NamedTrajectory) + if P.freetime + return P.unitary_components, P.drive_components, traj.components[traj.timestep] + else + return P.unitary_components, P.drive_components + end +end + +function (integrator::UnitaryPadeIntegrator)( + sys::AbstractQuantumSystem, + traj::NamedTrajectory; + unitary_name::Union{Symbol, Nothing}=nothing, + drive_name::Union{Symbol, Tuple{Vararg{Symbol}}, Nothing}=nothing, + order::Int=integrator.order, + G::Function=integrator.G, + ∂G::Function=integrator.∂G, + autodiff::Bool=integrator.autodiff +) + @assert !isnothing(unitary_name) "unitary_name must be provided" + @assert !isnothing(drive_name) "drive_name must be provided" + return UnitaryPadeIntegrator( + sys, + unitary_name, + drive_name, + traj; + order=order, + G=G, + ∂G=∂G, + autodiff=autodiff + ) +end + +# ------------------- Integrator ------------------- + + + +function nth_order_pade( + P::UnitaryPadeIntegrator, + Ũ⃗ₜ₊₁::AbstractVector, + Ũ⃗ₜ::AbstractVector, + aₜ::AbstractVector, + Δt::Real +) + Gₜ = P.G(aₜ) + + F, B = pade_operators(Gₜ, P.order, I(2P.ketdim), Δt) + + I_N = sparse(I, P.ketdim, P.ketdim) + + return (I_N ⊗ B) * Ũ⃗ₜ₊₁ - (I_N ⊗ F) * Ũ⃗ₜ +end + +@views function(P::UnitaryPadeIntegrator)( + zₜ::AbstractVector, + zₜ₊₁::AbstractVector, + t::Int +) + Ũ⃗ₜ₊₁ = zₜ₊₁[P.unitary_components] + Ũ⃗ₜ = zₜ[P.unitary_components] + aₜ = zₜ[P.drive_components] + + if P.freetime + Δtₜ = zₜ[P.timestep] + else + Δtₜ = P.timestep + end + + + return nth_order_pade(P, Ũ⃗ₜ₊₁, Ũ⃗ₜ, aₜ, Δtₜ) +end + +# ------------------- Jacobians ------------------- + +# aₜ should be a vector with all the controls. concatenate all the named traj controls +function ∂aₜ( + P::UnitaryPadeIntegrator, + G_powers::Vector{<:AbstractMatrix}, + Ũ⃗ₜ₊₁::AbstractVector, + Ũ⃗ₜ::AbstractVector, + aₜ::AbstractVector, + Δtₜ::Real +) + ∂aP = zeros(eltype(Ũ⃗ₜ), P.dim, P.n_drives) + + ∂G_∂aₜ = P.∂G(aₜ) + + I_N = sparse(I, P.ketdim, P.ketdim) + + for j = 1:P.n_drives + + # TODO: maybe rework for arbitrary drive indices eventually + + ∂aₜʲF = ∂aʲF(P, G_powers, Δtₜ, ∂G_∂aₜ[j]) + ∂aₜʲB = ∂aʲB(P, G_powers, Δtₜ, ∂G_∂aₜ[j]) + + ∂aP[:, j] = (I_N ⊗ ∂aₜʲB) * Ũ⃗ₜ₊₁ - (I_N ⊗ ∂aₜʲF) * Ũ⃗ₜ + end + + return ∂aP +end + + +function ∂Δtₜ( + P::UnitaryPadeIntegrator, + Gₜ_powers::Vector{<:AbstractMatrix}, + Ũ⃗ₜ₊₁::AbstractVector, + Ũ⃗ₜ::AbstractVector, + Δtₜ::Real +) + ∂ΔtₜF_coeffs, ∂ΔtₜB_coeffs = pade_coefficients(Δtₜ, P.order; + timestep_derivative=true + ) + + ∂ΔtₜF = sum(∂ΔtₜF_coeffs .* Gₜ_powers) + ∂ΔtₜB = sum(∂ΔtₜB_coeffs .* Gₜ_powers) + + I_N = sparse(I, P.ketdim, P.ketdim) + + return (I_N ⊗ ∂ΔtₜB) * Ũ⃗ₜ₊₁ - (I_N ⊗ ∂ΔtₜF) * Ũ⃗ₜ +end + +@views function jacobian( + P::UnitaryPadeIntegrator, + zₜ::AbstractVector, + zₜ₊₁::AbstractVector, + t::Int +) + # obtain state and control vectors + Ũ⃗ₜ₊₁ = zₜ₊₁[P.unitary_components] + Ũ⃗ₜ = zₜ[P.unitary_components] + aₜ = zₜ[P.drive_components] + + Gₜ = P.G(aₜ) + + # obtain timestep + if P.freetime + Δtₜ = zₜ[P.timestep] + else + Δtₜ = P.timestep + end + + Gₜ_powers = compute_powers(Gₜ, P.order ÷ 2) + + ∂aₜP = ∂aₜ(P, Gₜ_powers, Ũ⃗ₜ₊₁, Ũ⃗ₜ, aₜ, Δtₜ) + + Id = sparse(I, P.ketdim, P.ketdim) + + Fₜ, Bₜ = pade_operators(Gₜ_powers, I(2P.ketdim), Δtₜ) + + ∂Ũ⃗ₜP = -Id ⊗ Fₜ + ∂Ũ⃗ₜ₊₁P = Id ⊗ Bₜ + + if P.freetime + ∂ΔtₜP = ∂Δtₜ(P, Gₜ_powers, Ũ⃗ₜ₊₁, Ũ⃗ₜ, Δtₜ) + return ∂Ũ⃗ₜP, ∂Ũ⃗ₜ₊₁P, ∂aₜP, ∂ΔtₜP + else + return ∂Ũ⃗ₜP, ∂Ũ⃗ₜ₊₁P, ∂aₜP + end +end +# ---------------------------------------------------------------- +# Quantum State Pade Integrator +# ---------------------------------------------------------------- struct QuantumStatePadeIntegrator <: QuantumPadeIntegrator - I_2N::SparseMatrixCSC{Float64, Int} - G_drift::Matrix{Float64} - G_drives::Vector{Matrix{Float64}} - G_drive_anticomms::Union{Symmetric, Nothing} - G_drift_anticomms::Union{Vector{Matrix{Float64}}, Nothing} - state_symb::Union{Symbol,Nothing} - drive_symb::Union{Symbol,Tuple{Vararg{Symbol}},Nothing} + state_components::Vector{Int} + drive_components::Vector{Int} + timestep::Union{Real, Int} # either the timestep or the index of the timestep + freetime::Bool n_drives::Int - N::Int + ketdim::Int dim::Int + zdim::Int order::Int autodiff::Bool G::Function + ∂G::Function """ QuantumStatePadeIntegrator( sys::AbstractQuantumSystem, - state_symb::Union{Symbol,Nothing}=nothing, - drive_symb::Union{Symbol,Tuple{Vararg{Symbol}},Nothing}=nothing, - timestep_symb::Union{Symbol,Nothing}=nothing; + state_name::Union{Symbol,Nothing}=nothing, + drive_name::Union{Symbol,Tuple{Vararg{Symbol}},Nothing}=nothing, + timestep_name::Union{Symbol,Nothing}=nothing; order::Int=4, autodiff::Bool=false ) where R <: Real @@ -177,99 +516,97 @@ struct QuantumStatePadeIntegrator <: QuantumPadeIntegrator # Arguments - `sys::AbstractQuantumSystem`: the quantum system - - `state_symb::Symbol`: the symbol for the quantum state - - `drive_symb::Union{Symbol,Tuple{Vararg{Symbol}}}`: the symbol(s) for the drives + - `state_name::Symbol`: the nameol for the quantum state + - `drive_name::Union{Symbol,Tuple{Vararg{Symbol}}}`: the nameol(s) for the drives - `order::Int=4`: the order of the Pade approximation. Must be in `[4, 6, 8, 10]`. If order is not `4` and `autodiff` is `false`, then the integrator will use the hand-coded fourth order derivatives. - `autodiff::Bool=false`: whether to use automatic differentiation to compute the jacobian and hessian of the lagrangian """ function QuantumStatePadeIntegrator( sys::AbstractQuantumSystem, - state_symb::Union{Symbol,Nothing}=nothing, - drive_symb::Union{Symbol,Tuple{Vararg{Symbol}},Nothing}=nothing; + state_name::Symbol, + drive_name::Union{Symbol,Tuple{Vararg{Symbol}}}, + traj::NamedTrajectory; order::Int=4, - autodiff::Bool=order != 4, - G::Function=G_bilinear, + G::Function=a -> G_bilinear(a, sys.G_drift, sys.G_drives), + ∂G::Function=a -> sys.G_drives, + autodiff::Bool=false, ) - @assert order ∈ [4, 6, 8, 10] "order must be in [4, 6, 8, 10]" - @assert !isnothing(state_symb) "state_symb must be specified" - @assert !isnothing(drive_symb) "drive_symb must be specified" - n_drives = length(sys.H_drives) - N = size(sys.H_drift, 1) - dim = 2N - I_2N = sparse(I(2N)) + @assert order ∈ keys(PADE_COEFFICIENTS) "order ∉ $(keys(PADE_COEFFICIENTS))" + + ketdim = size(sys.H_drift, 1) + dim = 2ketdim + + state_components = traj.components[state_name] + + if drive_name isa Tuple + drive_components = vcat((traj.components[s] for s ∈ drive_name)...) + else + drive_components = traj.components[drive_name] + end + + n_drives = length(drive_components) + + @assert all(diff(drive_components) .== 1) "controls must be in order" + + freetime = traj.timestep isa Symbol - G_drift = sys.G_drift - G_drives = sys.G_drives + if freetime + timestep = traj.components[traj.timestep][1] + else + timestep = traj.timestep + end - drive_anticomms, drift_anticomms = - order == 4 ? build_anticomms(G_drift, G_drives, n_drives) : (nothing, nothing) return new( - I_2N, - G_drift, - G_drives, - drive_anticomms, - drift_anticomms, - state_symb, - drive_symb, + state_components, + drive_components, + timestep, + freetime, n_drives, - N, + ketdim, dim, + traj.dim, order, autodiff, - G + G, + ∂G ) end end -state(P::QuantumStatePadeIntegrator) = P.state_symb -controls(P::QuantumStatePadeIntegrator) = P.drive_symb - -function nth_order_pade( - P::UnitaryPadeIntegrator, - Ũ⃗ₜ₊₁::AbstractVector, - Ũ⃗ₜ::AbstractVector, - aₜ::AbstractVector, - Δt::Real -) - Ũₜ₊₁ = iso_vec_to_iso_operator(Ũ⃗ₜ₊₁) - Ũₜ = iso_vec_to_iso_operator(Ũ⃗ₜ) - Gₜ = P.G(aₜ, P.G_drift, P.G_drives) - n = P.order ÷ 2 - Gₜ_powers = compute_powers(Gₜ, n) - B = P.I_2N + sum([ - (-1)^k * PADE_COEFFICIENTS[P.order][k] * Δt^k * Gₜ_powers[k] - for k = 1:n - ]) - F = P.I_2N + sum([ - PADE_COEFFICIENTS[P.order][k] * Δt^k * Gₜ_powers[k] - for k = 1:n - ]) - δŨ = B * Ũₜ₊₁ - F * Ũₜ - return iso_operator_to_iso_vec(δŨ) -end - -@views function(P::UnitaryPadeIntegrator)( - zₜ::AbstractVector, - zₜ₊₁::AbstractVector, - traj::NamedTrajectory -) - Ũ⃗ₜ₊₁ = zₜ₊₁[traj.components[P.unitary_symb]] - Ũ⃗ₜ = zₜ[traj.components[P.unitary_symb]] - if traj.timestep isa Symbol - Δtₜ = zₜ[traj.components[traj.timestep]][1] +function get_comps(P::QuantumStatePadeIntegrator, traj::NamedTrajectory) + if P.freetime + return P.state_components, P.drive_components, traj.components[traj.timestep] else - Δtₜ = traj.timestep + return P.state_components, P.drive_components end - if P.drive_symb isa Tuple - aₜ = vcat([zₜ[traj.components[s]] for s in P.drive_symb]...) - else - aₜ = zₜ[traj.components[P.drive_symb]] - end - return nth_order_pade(P, Ũ⃗ₜ₊₁, Ũ⃗ₜ, aₜ, Δtₜ) end +function (integrator::QuantumStatePadeIntegrator)( + sys::AbstractQuantumSystem, + traj::NamedTrajectory; + state_name::Union{Symbol, Nothing}=nothing, + drive_name::Union{Symbol, Tuple{Vararg{Symbol}}, Nothing}=nothing, + order::Int=integrator.order, + G::Function=integrator.G, + ∂G::Function=integrator.∂G, + autodiff::Bool=integrator.autodiff +) + @assert !isnothing(state_name) "state_name must be provided" + @assert !isnothing(drive_name) "drive_name must be provided" + return QuantumStatePadeIntegrator( + sys, + state_name, + drive_name, + traj; + order=order, + G=G, + ∂G=∂G, + autodiff=autodiff + ) +end +# ------------------- Integrator ------------------- function nth_order_pade( P::QuantumStatePadeIntegrator, @@ -278,268 +615,106 @@ function nth_order_pade( aₜ::AbstractVector, Δt::Real ) - Gₜ = P.G(aₜ, P.G_drift, P.G_drives) - n = P.order ÷ 2 - Gₜ_powers = compute_powers(Gₜ, n) - B = P.I_2N + sum([ - (-1)^k * PADE_COEFFICIENTS[P.order][k] * Δt^k * Gₜ_powers[k] - for k = 1:n - ]) - F = P.I_2N + sum([ - PADE_COEFFICIENTS[P.order][k] * Δt^k * Gₜ_powers[k] - for k = 1:n - ]) - δψ̃ = B * ψ̃ₜ₊₁ - F * ψ̃ₜ - return δψ̃ + Gₜ = P.G(aₜ) + + F, B = pade_operators(Gₜ, P.order, I(2P.ketdim), Δt) + + return B * ψ̃ₜ₊₁ - F * ψ̃ₜ end @views function(P::QuantumStatePadeIntegrator)( zₜ::AbstractVector, zₜ₊₁::AbstractVector, - traj::NamedTrajectory + t::Int ) - ψ̃ₜ₊₁ = zₜ₊₁[traj.components[P.state_symb]] - ψ̃ₜ = zₜ[traj.components[P.state_symb]] - if P.drive_symb isa Tuple - aₜ = vcat([zₜ[traj.components[s]] for s in P.drive_symb]...) - else - aₜ = zₜ[traj.components[P.drive_symb]] - end - if traj.timestep isa Symbol - Δtₜ = zₜ[traj.components[traj.timestep]][1] - else - Δtₜ = traj.timestep - end - return nth_order_pade(P, ψ̃ₜ₊₁, ψ̃ₜ, aₜ, Δtₜ) -end - -# aₜ should be a vector with all the controls. concatenate all the named traj controls -function ∂aₜ( - P::UnitaryPadeIntegrator, - Ũ⃗ₜ₊₁::AbstractVector{T}, - Ũ⃗ₜ::AbstractVector{T}, - aₜ::AbstractVector{T}, - Δtₜ::Real, -) where T <: Real - - if P.autodiff || !isnothing(P.G) - - # then we need to use the nth_order_pade function - # which handles nonlinear G and higher order Pade integrators + ψ̃ₜ₊₁ = zₜ₊₁[P.state_components] + ψ̃ₜ = zₜ[P.state_components] + aₜ = zₜ[P.drive_components] - f(a) = nth_order_pade(P, Ũ⃗ₜ₊₁, Ũ⃗ₜ, a, Δtₜ) - ∂aP = ForwardDiff.jacobian(f, aₜ) - - # otherwise we don't have a nonlinear G or are fine with using - # the fourth order derivatives - - elseif P.order == 4 - n_drives = length(aₜ) - ∂aP = Array{T}(undef, P.dim, n_drives) - isodim = 2*P.N - for j = 1:n_drives - Gʲ = P.G_drives[j] - Gʲ_anticomm_Gₜ = - P.G(aₜ, P.G_drift_anticomms[j], P.G_drive_anticomms[:, j]) - for i = 0:P.N-1 - ψ̃ⁱₜ₊₁ = @view Ũ⃗ₜ₊₁[i * isodim .+ (1:isodim)] - ψ̃ⁱₜ = @view Ũ⃗ₜ[i * isodim .+ (1:isodim)] - ∂aP[i*isodim .+ (1:isodim), j] = - -Δtₜ / 2 * Gʲ * (ψ̃ⁱₜ₊₁ + ψ̃ⁱₜ) + - Δtₜ^2 / 12 * Gʲ_anticomm_Gₜ * (ψ̃ⁱₜ₊₁ - ψ̃ⁱₜ) - end - end + if P.freetime + Δtₜ = zₜ[P.timestep] else - ## higher order pade code goes here + Δtₜ = P.timestep end - return ∂aP + return nth_order_pade(P, ψ̃ₜ₊₁, ψ̃ₜ, aₜ, Δtₜ) end function ∂aₜ( P::QuantumStatePadeIntegrator, - ψ̃ₜ₊₁::AbstractVector{T}, - ψ̃ₜ::AbstractVector{T}, - aₜ::AbstractVector{T}, - Δtₜ::Real, -) where T <: Real - if P.autodiff || !isnothing(P.G) + G_powers::Vector{<:AbstractMatrix}, + ψ̃ₜ₊₁::AbstractVector, + ψ̃ₜ::AbstractVector, + aₜ::AbstractVector, + Δtₜ::Real +) + ∂aP = zeros(eltype(ψ̃ₜ), P.dim, P.n_drives) - # then we need to use the nth_order_pade function - # which handles nonlinear G and higher order Pade integrators + ∂G_∂aₜ = P.∂G(aₜ) - f(a) = nth_order_pade(P, ψ̃ₜ₊₁, ψ̃ₜ, a, Δtₜ) - ∂aP = ForwardDiff.jacobian(f, aₜ) + for j = 1:P.n_drives - # otherwise we don't have a nonlinear G or are fine with using - # the fourth order derivatives + ∂aₜʲF = ∂aʲF(P, G_powers, Δtₜ, ∂G_∂aₜ[j]) + ∂aₜʲB = ∂aʲB(P, G_powers, Δtₜ, ∂G_∂aₜ[j]) - elseif P.order == 4 - n_drives = length(aₜ) - ∂aP = zeros(P.dim, n_drives) - for j = 1:n_drives - Gʲ = P.G_drives[j] - Gʲ_anticomm_Gₜ = - P.G(aₜ, P.G_drift_anticomms[j], P.G_drive_anticomms[:, j]) - ∂aP[:, j] = - -Δtₜ / 2 * Gʲ * (ψ̃ₜ₊₁ + ψ̃ₜ) + - Δtₜ^2 / 12 * Gʲ_anticomm_Gₜ * (ψ̃ₜ₊₁ - ψ̃ₜ) - end - else - ### code for arbitrary Pade goes here + ∂aP[:, j] = ∂aₜʲB * ψ̃ₜ₊₁ - ∂aₜʲF * ψ̃ₜ end + return ∂aP end - -function ∂Δtₜ( - P::UnitaryPadeIntegrator, - Ũ⃗ₜ₊₁::AbstractVector, - Ũ⃗ₜ::AbstractVector, - aₜ::AbstractVector, - Δtₜ::Real -) - Gₜ = P.G(aₜ, P.G_drift, P.G_drives) - Ũₜ₊₁ = iso_vec_to_iso_operator(Ũ⃗ₜ₊₁) - Ũₜ = iso_vec_to_iso_operator(Ũ⃗ₜ) - if P.order == 4 - ∂ΔtₜP_operator = -1/2 * Gₜ * (Ũₜ₊₁ + Ũₜ) + 1/6 * Δtₜ * Gₜ^2 * (Ũₜ₊₁ - Ũₜ) - ∂ΔtₜP = iso_operator_to_iso_vec(∂ΔtₜP_operator) - else - n = P.order ÷ 2 - Gₜ_powers = compute_powers(Gₜ, n) - B = sum([ - (-1)^k * k * PADE_COEFFICIENTS[P.order][k] * Δtₜ^(k-1) * Gₜ_powers[k] - for k = 1:n - ]) - F = sum([ - k * PADE_COEFFICIENTS[P.order][k] * Δtₜ^(k-1) * Gₜ_powers[k] - for k = 1:n - ]) - ∂ΔtₜP_operator = B * Ũₜ₊₁ - F * Ũₜ - ∂ΔtₜP = iso_operator_to_iso_vec(∂ΔtₜP_operator) - end - - return ∂ΔtₜP -end - function ∂Δtₜ( P::QuantumStatePadeIntegrator, + Gₜ_powers::Vector{<:AbstractMatrix}, ψ̃ₜ₊₁::AbstractVector, ψ̃ₜ::AbstractVector, - aₜ::AbstractVector, Δtₜ::Real ) + ∂ΔtₜF_coeffs, ∂ΔtₜB_coeffs = pade_coefficients(Δtₜ, P.order; + timestep_derivative=true + ) - Gₜ = P.G(aₜ, P.G_drift, P.G_drives) - if P.order==4 - ∂ΔtₜP = -1/2 * Gₜ * (ψ̃ₜ₊₁ + ψ̃ₜ) + 1/6 * Δtₜ * Gₜ^2 * (ψ̃ₜ₊₁ - ψ̃ₜ) - else - n = P.order ÷ 2 - Gₜ_powers = [Gₜ^i for i in 1:n] - B = sum([(-1)^k * k * PADE_COEFFICIENTS[P.order][k] * Δtₜ^(k-1) * Gₜ_powers[k] for k = 1:n]) - F = sum([k * PADE_COEFFICIENTS[P.order][k] * Δtₜ^(k-1) * Gₜ_powers[k] for k = 1:n]) - ∂ΔtₜP = B*ψ̃ₜ₊₁ - F*ψ̃ₜ - end - return ∂ΔtₜP -end - - -@views function jacobian( - P::UnitaryPadeIntegrator, - zₜ::AbstractVector{T}, - zₜ₊₁::AbstractVector{T}, - traj::NamedTrajectory -) where T <: Number - free_time = traj.timestep isa Symbol - - Ũ⃗ₜ₊₁ = zₜ₊₁[traj.components[P.unitary_symb]] - Ũ⃗ₜ = zₜ[traj.components[P.unitary_symb]] - - Δtₜ = free_time ? zₜ[traj.components[traj.timestep]][1] : traj.timestep - - if P.drive_symb isa Tuple - inds = [traj.components[s] for s in P.drive_symb] - inds = vcat(collect.(inds)...) - else - inds = traj.components[P.drive_symb] - end - - for i = 1:length(inds) - 1 - @assert inds[i] + 1 == inds[i + 1] "Controls must be in order" - end - - aₜ = zₜ[inds] - ∂aₜP = ∂aₜ(P, Ũ⃗ₜ₊₁, Ũ⃗ₜ, aₜ, Δtₜ) - if free_time - ∂ΔtₜP = ∂Δtₜ(P, Ũ⃗ₜ₊₁, Ũ⃗ₜ, aₜ, Δtₜ) - end - - ∂Ũ⃗ₜP = spzeros(T, P.dim, P.dim) - ∂Ũ⃗ₜ₊₁P = spzeros(T, P.dim, P.dim) - Gₜ = P.G(aₜ, P.G_drift, P.G_drives) - n = P.order ÷ 2 - - # can memoize this chunk of code, prly memoize G powers - Gₜ_powers = compute_powers(Gₜ, n) - B = P.I_2N + sum([(-1)^k * PADE_COEFFICIENTS[P.order][k] * Δtₜ^k * Gₜ_powers[k] for k = 1:n]) - F = P.I_2N + sum([PADE_COEFFICIENTS[P.order][k] * Δtₜ^k * Gₜ_powers[k] for k = 1:n]) - - ∂Ũ⃗ₜ₊₁P = blockdiag(fill(sparse(B), P.N)...) - ∂Ũ⃗ₜP = blockdiag(fill(sparse(-F), P.N)...) + ∂ΔtₜF = sum(∂ΔtₜF_coeffs .* Gₜ_powers) + ∂ΔtₜB = sum(∂ΔtₜB_coeffs .* Gₜ_powers) - if free_time - return ∂Ũ⃗ₜP, ∂Ũ⃗ₜ₊₁P, ∂aₜP, ∂ΔtₜP - else - return ∂Ũ⃗ₜP, ∂Ũ⃗ₜ₊₁P, ∂aₜP - end + return ∂ΔtₜB * ψ̃ₜ₊₁ - ∂ΔtₜF * ψ̃ₜ end @views function jacobian( P::QuantumStatePadeIntegrator, zₜ::AbstractVector, zₜ₊₁::AbstractVector, - traj::NamedTrajectory + t::Int ) - free_time = traj.timestep isa Symbol - - ψ̃ₜ₊₁ = zₜ₊₁[traj.components[P.state_symb]] - ψ̃ₜ = zₜ[traj.components[P.state_symb]] + # obtain state and control vectors + ψ̃ₜ₊₁ = zₜ₊₁[P.state_components] + ψ̃ₜ = zₜ[P.state_components] + aₜ = zₜ[P.drive_components] + Gₜ = P.G(aₜ) - Δtₜ = free_time ? zₜ[traj.components[traj.timestep]][1] : traj.timestep - - if P.drive_symb isa Tuple - inds = [traj.components[s] for s in P.drive_symb] - inds = vcat(collect.(inds)...) + # obtain timestep + if P.freetime + Δtₜ = zₜ[P.timestep] else - inds = traj.components[P.drive_symb] - end - - for i = 1:length(inds) - 1 - @assert inds[i] + 1 == inds[i + 1] "Controls must be in order" + Δtₜ = P.timestep end - aₜ = zₜ[inds] + Gₜ_powers = compute_powers(Gₜ, P.order ÷ 2) - ∂aₜP = ∂aₜ(P, ψ̃ₜ₊₁, ψ̃ₜ, aₜ, Δtₜ) - if free_time - ∂ΔtₜP = ∂Δtₜ(P, ψ̃ₜ₊₁, ψ̃ₜ, aₜ, Δtₜ) - end + ∂aₜP = ∂aₜ(P, Gₜ_powers, ψ̃ₜ₊₁, ψ̃ₜ, aₜ, Δtₜ) - Gₜ = P.G(aₜ, P.G_drift, P.G_drives) - n = P.order ÷ 2 - Gₜ_powers = compute_powers(Gₜ, n) - B = P.I_2N + sum([(-1)^k * PADE_COEFFICIENTS[P.order][k] * Δtₜ^k * Gₜ_powers[k] for k = 1:n]) - F = P.I_2N + sum([PADE_COEFFICIENTS[P.order][k] * Δtₜ^k * Gₜ_powers[k] for k = 1:n]) + # jacobian wrt state + Fₜ, Bₜ = pade_operators(Gₜ_powers, I(2P.ketdim), Δtₜ) - ∂ψ̃ₜP = -F - ∂ψ̃ₜ₊₁P = B + ∂ψ̃ₜP = -Fₜ + ∂ψ̃ₜ₊₁P = Bₜ - if free_time + if P.freetime + ∂ΔtₜP = ∂Δtₜ(P, Gₜ_powers, ψ̃ₜ₊₁, ψ̃ₜ, Δtₜ) return ∂ψ̃ₜP, ∂ψ̃ₜ₊₁P, ∂aₜP, ∂ΔtₜP else return ∂ψ̃ₜP, ∂ψ̃ₜ₊₁P, ∂aₜP @@ -571,7 +746,7 @@ function μ∂aₜ∂Ũ⃗ₜ( Ĝʲ = P.G(aₜ, P.G_drift_anticomms[j], P.G_drive_anticomms[:, j]) ∂aₜ∂Ũ⃗ₜ_block_i = -(Δtₜ / 2 * Gʲ + Δtₜ^2 / 12 * Ĝʲ) # sparse is necessary since blockdiag doesn't accept dense matrices - ∂aₜ∂Ũ⃗ₜ = blockdiag(fill(sparse(∂aₜ∂Ũ⃗ₜ_block_i), P.N)...) + ∂aₜ∂Ũ⃗ₜ = blockdiag(fill(sparse(∂aₜ∂Ũ⃗ₜ_block_i), P.ketdim)...) μ∂aₜ∂Ũ⃗ₜP[:, j] = ∂aₜ∂Ũ⃗ₜ' * μₜ end else @@ -595,7 +770,7 @@ function μ∂Ũ⃗ₜ₊₁∂aₜ( Ĝʲ = P.G(aₜ, P.G_drift_anticomms[j], P.G_drive_anticomms[:, j]) ∂Ũ⃗ₜ₊₁∂aₜ_block_i = -Δtₜ / 2 * Gʲ + Δtₜ^2 / 12 * Ĝʲ # sparse is necessary since blockdiag doesn't accept dense matrices - ∂Ũ⃗ₜ₊₁∂aₜ = blockdiag(fill(sparse(∂Ũ⃗ₜ₊₁∂aₜ_block_i), P.N)...) + ∂Ũ⃗ₜ₊₁∂aₜ = blockdiag(fill(sparse(∂Ũ⃗ₜ₊₁∂aₜ_block_i), P.ketdim)...) μ∂Ũ⃗ₜ₊₁∂aₜP[j, :] = μₜ' * ∂Ũ⃗ₜ₊₁∂aₜ end @@ -659,7 +834,7 @@ function μ∂²aₜ( for j = 1:i ∂aʲ∂aⁱP_block = Δtₜ^2 / 12 * P.G_drive_anticomms[i, j] - ∂aʲ∂aⁱP = blockdiag(fill(sparse(∂aʲ∂aⁱP_block), P.N)...) + ∂aʲ∂aⁱP = blockdiag(fill(sparse(∂aʲ∂aⁱP_block), P.ketdim)...) μ∂²aₜP[j, i] = dot(μₜ, ∂aʲ∂aⁱP*(Ũ⃗ₜ₊₁ - Ũ⃗ₜ)) end end @@ -707,8 +882,8 @@ function μ∂Δtₜ∂aₜ( for j = 1:n_drives Gʲ = P.G_drives[j] Ĝʲ = P.G(aₜ, P.G_drift_anticomms[j], P.G_drive_anticomms[:, j]) - B = blockdiag(fill(sparse(-1/2 * Gʲ + 1/6 * Δtₜ * Ĝʲ), P.N)...) - F = blockdiag(fill(sparse(1/2 * Gʲ + 1/6 * Δtₜ * Ĝʲ), P.N)...) + B = blockdiag(fill(sparse(-1/2 * Gʲ + 1/6 * Δtₜ * Ĝʲ), P.ketdim)...) + F = blockdiag(fill(sparse(1/2 * Gʲ + 1/6 * Δtₜ * Ĝʲ), P.ketdim)...) ∂Δtₜ∂aₜ_j = B*Ũ⃗ₜ₊₁ - F*Ũ⃗ₜ μ∂Δtₜ∂aₜP[j] = dot(μₜ, ∂Δtₜ∂aₜ_j) end @@ -749,7 +924,7 @@ function μ∂Δtₜ∂Ũ⃗ₜ( ) Gₜ = P.G(aₜ, P.G_drift, P.G_drives) minus_F = -(1/2 * Gₜ + 1/6 * Δtₜ * Gₜ^2) - big_minus_F = blockdiag(fill(sparse(minus_F), P.N)...) + big_minus_F = blockdiag(fill(sparse(minus_F), P.ketdim)...) return big_minus_F' * μₜ end @@ -761,7 +936,7 @@ function μ∂Ũ⃗ₜ₊₁∂Δtₜ( ) Gₜ = P.G(aₜ, P.G_drift, P.G_drives) B = -1/2 * Gₜ + 1/6 * Δtₜ * Gₜ^2 - big_B = blockdiag(fill(sparse(B), P.N)...) + big_B = blockdiag(fill(sparse(B), P.ketdim)...) return μₜ' * big_B end @@ -797,7 +972,7 @@ function μ∂²Δtₜ( ) Gₜ = P.G(aₜ, P.G_drift, P.G_drives) ∂²Δtₜ_gen_block = 1/6 * Gₜ^2 - ∂²Δtₜ_gen = blockdiag(fill(sparse(∂²Δtₜ_gen_block), P.N)...) + ∂²Δtₜ_gen = blockdiag(fill(sparse(∂²Δtₜ_gen_block), P.ketdim)...) ∂²Δtₜ = ∂²Δtₜ_gen * (Ũ⃗ₜ₊₁ - Ũ⃗ₜ) return μₜ' * ∂²Δtₜ end @@ -823,16 +998,16 @@ end ) free_time = traj.timestep isa Symbol - Ũ⃗ₜ₊₁ = zₜ₊₁[traj.components[P.unitary_symb]] - Ũ⃗ₜ = zₜ[traj.components[P.unitary_symb]] + Ũ⃗ₜ₊₁ = zₜ₊₁[traj.components[P.unitary_name]] + Ũ⃗ₜ = zₜ[traj.components[P.unitary_name]] Δtₜ = free_time ? zₜ[traj.components[traj.timestep]][1] : traj.timestep - if P.drive_symb isa Tuple - inds = [traj.components[s] for s in P.drive_symb] + if P.drive_name isa Tuple + inds = [traj.components[s] for s in P.drive_name] inds = vcat(collect.(inds)...) else - inds = traj.components[P.drive_symb] + inds = traj.components[P.drive_name] end aₜ = zₜ[inds] @@ -876,16 +1051,16 @@ end ) free_time = traj.timestep isa Symbol - ψ̃ₜ₊₁ = zₜ₊₁[traj.components[P.state_symb]] - ψ̃ₜ = zₜ[traj.components[P.state_symb]] + ψ̃ₜ₊₁ = zₜ₊₁[traj.components[P.state_name]] + ψ̃ₜ = zₜ[traj.components[P.state_name]] Δtₜ = free_time ? zₜ[traj.components[traj.timestep]][1] : traj.timestep - if P.drive_symb isa Tuple - inds = [traj.components[s] for s in P.drive_symb] + if P.drive_name isa Tuple + inds = [traj.components[s] for s in P.drive_name] inds = vcat(collect.(inds)...) else - inds = traj.components[P.drive_symb] + inds = traj.components[P.drive_name] end aₜ = zₜ[inds] diff --git a/src/isomorphisms.jl b/src/isomorphisms.jl new file mode 100644 index 00000000..56fbbc59 --- /dev/null +++ b/src/isomorphisms.jl @@ -0,0 +1,205 @@ +module Isomorphisms + +export mat +export ket_to_iso +export iso_to_ket +export iso_vec_to_operator +export iso_vec_to_iso_operator +export operator_to_iso_vec +export iso_operator_to_iso_vec +export iso_operator_to_operator +export operator_to_iso_operator +export iso +export iso_dm +export ad_vec + +using LinearAlgebra +using TestItemRunner + + +@doc raw""" + mat(x::AbstractVector) + +Convert a vector `x` into a square matrix. The length of `x` must be a perfect square. +""" +function mat(x::AbstractVector) + n = isqrt(length(x)) + @assert n^2 == length(x) "Vector length must be a perfect square" + return reshape(x, n, n) +end + + +# ----------------------------------------------------------------------------- # +# Kets # +# ----------------------------------------------------------------------------- # + +@doc raw""" + ket_to_iso(ψ) + +Convert a ket vector `ψ` into a complex vector with real and imaginary parts. +""" +ket_to_iso(ψ) = [real(ψ); imag(ψ)] + +@doc raw""" + iso_to_ket(ψ̃) + +Convert a complex vector `ψ̃` with real and imaginary parts into a ket vector. +""" +iso_to_ket(ψ̃) = ψ̃[1:div(length(ψ̃), 2)] + im * ψ̃[(div(length(ψ̃), 2) + 1):end] + +# ----------------------------------------------------------------------------- # +# Unitaries # +# ----------------------------------------------------------------------------- # + +@doc raw""" + iso_vec_to_operator(Ũ⃗::AbstractVector) + +Convert a real vector `Ũ⃗` into a complex matrix representing an operator. + +Must be differentiable. +""" +function iso_vec_to_operator(Ũ⃗::AbstractVector{R}) where R + Ũ⃗_dim = div(length(Ũ⃗), 2) + N = Int(sqrt(Ũ⃗_dim)) + U = Matrix{complex(R)}(undef, N, N) + for i=0:N-1 + U[:, i+1] .= @view(Ũ⃗[i * 2N .+ (1:N)]) + one(R) * im * @view(Ũ⃗[i * 2N .+ (N+1:2N)]) + end + return U +end + +@doc raw""" + iso_vec_to_iso_operator(Ũ⃗::AbstractVector) + +Convert a real vector `Ũ⃗` into a real matrix representing an isomorphism operator. + +Must be differentiable. +""" +function iso_vec_to_iso_operator(Ũ⃗::AbstractVector{R}) where R + N = Int(sqrt(length(Ũ⃗) ÷ 2)) + Ũ = Matrix{R}(undef, 2N, 2N) + U_real = Matrix{R}(undef, N, N) + U_imag = Matrix{R}(undef, N, N) + for i=0:N-1 + U_real[:, i+1] .= @view(Ũ⃗[i*2N .+ (1:N)]) + U_imag[:, i+1] .= @view(Ũ⃗[i*2N .+ (N+1:2N)]) + end + Ũ[1:N, 1:N] .= U_real + Ũ[1:N, (N + 1):end] .= -U_imag + Ũ[(N + 1):end, 1:N] .= U_imag + Ũ[(N + 1):end, (N + 1):end] .= U_real + return Ũ +end + +@doc raw""" + operator_to_iso_vec(U::AbstractMatrix{<:Complex}) + +Convert a complex matrix `U` representing an operator into a real vector. + +Must be differentiable. +""" +function operator_to_iso_vec(U::AbstractMatrix{R}) where R + N = size(U,1) + Ũ⃗ = Vector{real(R)}(undef, N^2 * 2) + for i=0:N-1 + Ũ⃗[i*2N .+ (1:N)] .= real(@view(U[:, i+1])) + Ũ⃗[i*2N .+ (N+1:2N)] .= imag(@view(U[:, i+1])) + end + return Ũ⃗ +end + +@doc raw""" + iso_operator_to_iso_vec(Ũ::AbstractMatrix) + +Convert a real matrix `Ũ` representing an isomorphism operator into a real vector. + +Must be differentiable. +""" +function iso_operator_to_iso_vec(Ũ::AbstractMatrix{R}) where R + N = size(Ũ, 1) ÷ 2 + Ũ⃗ = Vector{R}(undef, N^2 * 2) + for i=0:N-1 + Ũ⃗[i*2N .+ (1:2N)] .= @view Ũ[:, i+1] + end + return Ũ⃗ +end + +iso_operator_to_operator(Ũ) = iso_vec_to_operator(iso_operator_to_iso_vec(Ũ)) + +operator_to_iso_operator(U) = iso_vec_to_iso_operator(operator_to_iso_vec(U)) + +# ----------------------------------------------------------------------------- # +# Open systems +# ----------------------------------------------------------------------------- # + +function ad_vec(H::AbstractMatrix{<:Number}; anti::Bool=false) + Id = sparse(eltype(H), I, size(H)...) + return Id ⊗ H - (-1)^anti * conj(H)' ⊗ Id +end + +# ----------------------------------------------------------------------------- # +# Hamiltonians +# ----------------------------------------------------------------------------- # + +const Im2 = [ + 0 -1; + 1 0 +] + +@doc raw""" + G(H::AbstractMatrix)::Matrix{Float64} + +Returns the isomorphism of ``-iH``: + +```math +G(H) = \widetilde{- i H} = \mqty(1 & 0 \\ 0 & 1) \otimes \Im(H) - \mqty(0 & -1 \\ 1 & 0) \otimes \Re(H) +``` + +where ``\Im(H)`` and ``\Re(H)`` are the imaginary and real parts of ``H`` and the tilde indicates the standard isomorphism of a complex valued matrix: + +```math +\widetilde{H} = \mqty(1 & 0 \\ 0 & 1) \otimes \Re(H) + \mqty(0 & -1 \\ 1 & 0) \otimes \Im(H) +``` +""" +G(H::AbstractMatrix{<:Number}) = kron(I(2), imag(H)) - kron(Im2, real(H)) + +iso(H::AbstractMatrix{<:Number}) = kron(I(2), real(H)) + kron(Im2, imag(H)) + + +""" + H(G::AbstractMatrix{<:Number})::Matrix{ComplexF64} + +Returns the inverse of `G(H) = iso(-iH)`, i.e. returns H + +""" +function H(G::AbstractMatrix{<:Number}) + dim = size(G, 1) ÷ 2 + H_imag = G[1:dim, 1:dim] + H_real = -G[dim+1:end, 1:dim] + return H_real + 1.0im * H_imag +end + +""" + iso_dm(ρ::AbstractMatrix) + +returns the isomorphism `ρ⃗̃ = ket_to_iso(vec(ρ))` of a density matrix `ρ` +""" +iso_dm(ρ::AbstractMatrix) = ket_to_iso(vec(ρ)) + + + +# =========================================================================== # + +@testitem "Test isomorphism utilities" begin + using LinearAlgebra + iso_vec = [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0] + @test mat([1.0, 2.0, 3.0, 4.0]) == [1.0 3.0; 2.0 4.0] + @test ket_to_iso([1.0, 2.0]) == [1.0, 2.0, 0.0, 0.0] + @test iso_to_ket([1.0, 2.0, 0.0, 0.0]) == [1.0, 2.0] + @test iso_vec_to_operator(iso_vec) == [1.0 0.0; 0.0 1.0] + @test iso_vec_to_iso_operator(iso_vec) == [1.0 0.0 -0.0 -0.0; 0.0 1.0 -0.0 -0.0; 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 1.0] + @test operator_to_iso_vec(Complex[1.0 0.0; 0.0 1.0]) == iso_vec + @test iso_operator_to_iso_vec(iso_vec_to_iso_operator(iso_vec)) == iso_vec +end + +end diff --git a/src/losses.jl b/src/losses.jl deleted file mode 100644 index 5c71d79f..00000000 --- a/src/losses.jl +++ /dev/null @@ -1,645 +0,0 @@ -module Losses - -export Loss - -export QuantumLoss -export QuantumLossGradient -export QuantumLossHessian - -export InfidelityLoss -export UnitaryInfidelityLoss -export UnitaryTraceLoss - -export infidelity -export unitary_infidelity - -using ..QuantumUtils -using ..QuantumSystems -using ..StructureUtils - - -using NamedTrajectories - -using TrajectoryIndexingUtils -using LinearAlgebra -using SparseArrays -using ForwardDiff -using Symbolics - -# -# loss functions -# - - -# TODO: renormalize vectors in place of abs -# ⋅ penalize loss to remain near unit norm -# ⋅ Σ α * (1 - ψ̃'ψ̃), α = 1e-3 - -abstract type AbstractLoss end - -""" - infidelity(ψ̃::AbstractVector, ψ̃goal::AbstractVector) - -Returns the infidelity between two quantum statevectors specified -in the ``\\mathbb{C}^n \\to \\mathbb{R}^{2n}`` isomorphism space. - -""" -function infidelity(ψ̃::AbstractVector, ψ̃goal::AbstractVector) - ψ = iso_to_ket(ψ̃) - ψgoal = iso_to_ket(ψ̃goal) - return abs(1 - abs2(ψgoal'ψ)) -end - -@doc raw""" - isovec_unitary_fidelity(Ũ::AbstractVector, Ũgoal::AbstractVector) - -Returns the fidelity between the isomorphic unitary $\vec{\widetilde{U}} \sim U \in SU(n)$ and the isomorphic goal unitary $\vec{\widetilde{U}}_{\text{goal}}$. - -```math -\begin{aligned} -\mathcal{F}(\vec{\widetilde{U}}, \vec{\widetilde{U}}_{\text{goal}}) &= \frac{1}{n} \abs{\tr \qty(U_{\text{goal}}^\dagger U)} \\ -&= \frac{1}{n} \sqrt{T_R^{2} + T_I^{2}} -\end{aligned} -``` - -where $T_R = \langle \vec{\widetilde{U}}_{\text{goal}, R}, \vec{\widetilde{U}}_R \rangle + \langle \vec{\widetilde{U}}_{\text{goal}, I}, \vec{\widetilde{U}}_I \rangle$ and $T_I = \langle \vec{\widetilde{U}}_{\text{goal}, R}, \vec{\widetilde{U}}_I \rangle - \langle \vec{\widetilde{U}}_{\text{goal}, I}, \vec{\widetilde{U}}_R \rangle$. - -""" -@inline @views function isovec_unitary_fidelity( - Ũ⃗::AbstractVector, - Ũ⃗_goal::AbstractVector, - subspace::Tuple{AbstractVector{Int},AbstractVector{Int}}=axes(iso_vec_to_operator(Ũ⃗_goal)) -) - subspace_rows, subspace_cols = subspace - U = iso_vec_to_operator(Ũ⃗)[subspace_rows, subspace_cols] - n = size(U, 1) - U_goal = iso_vec_to_operator(Ũ⃗_goal)[subspace_rows, subspace_cols] - U⃗ᵣ, U⃗ᵢ = vec(real(U)), vec(imag(U)) - Ū⃗ᵣ, Ū⃗ᵢ = vec(real(U_goal)), vec(imag(U_goal)) - Tᵣ = Ū⃗ᵣ' * U⃗ᵣ + Ū⃗ᵢ' * U⃗ᵢ - Tᵢ = Ū⃗ᵣ' * U⃗ᵢ - Ū⃗ᵢ' * U⃗ᵣ - return 1 / n * sqrt(Tᵣ^2 + Tᵢ^2) -end - -@views function unitary_infidelity( - Ũ⃗::AbstractVector, - Ũ⃗_goal::AbstractVector, - subspace::Tuple{AbstractVector{Int},AbstractVector{Int}}= - axes(iso_vec_to_operator(Ũ⃗_goal)) -) - ℱ = isovec_unitary_fidelity(Ũ⃗, Ũ⃗_goal, subspace) - return abs(1 - ℱ) -end - -@views function unitary_infidelity_gradient( - Ũ⃗::AbstractVector, - Ũ⃗_goal::AbstractVector, - subspace::Tuple{AbstractVector{Int},AbstractVector{Int}}= - axes(iso_vec_to_operator(Ũ⃗_goal)) -) - subspace_rows, subspace_cols = subspace - U = iso_vec_to_operator(Ũ⃗)[subspace_rows, subspace_cols] - n = size(U, 1) - U_goal = iso_vec_to_operator(Ũ⃗_goal)[subspace_rows, subspace_cols] - U⃗ᵣ, U⃗ᵢ = vec(real(U)), vec(imag(U)) - Ū⃗ᵣ, Ū⃗ᵢ = vec(real(U_goal)), vec(imag(U_goal)) - Tᵣ = Ū⃗ᵣ' * U⃗ᵣ + Ū⃗ᵢ' * U⃗ᵢ - Tᵢ = Ū⃗ᵣ' * U⃗ᵢ - Ū⃗ᵢ' * U⃗ᵣ - ℱ = 1 / n * sqrt(Tᵣ^2 + Tᵢ^2) - ∇ᵣℱ = 1 / (n^2 * ℱ) * (Tᵣ * Ū⃗ᵣ - Tᵢ * Ū⃗ᵢ) - ∇ᵢℱ = 1 / (n^2 * ℱ) * (Tᵣ * Ū⃗ᵢ + Tᵢ * Ū⃗ᵣ) - ∇ℱ = [∇ᵣℱ; ∇ᵢℱ] - permutation = vcat(vcat([[slice(j, n), slice(j, n) .+ n^2] for j = 1:n]...)...) - ∇ℱ = ∇ℱ[permutation] - return -sign(1 - ℱ) * ∇ℱ -end - -@views function unitary_infidelity_hessian( - Ũ⃗::AbstractVector, - Ũ⃗_goal::AbstractVector, - subspace::Tuple{AbstractVector{Int},AbstractVector{Int}}=axes(iso_vec_to_operator(Ũ⃗_goal)) -) - subspace_rows, subspace_cols = subspace - U = iso_vec_to_operator(Ũ⃗)[subspace_rows, subspace_cols] - n = size(U, 1) - U_goal = iso_vec_to_operator(Ũ⃗_goal)[subspace_rows, subspace_cols] - U⃗ᵣ, U⃗ᵢ = vec(real(U)), vec(imag(U)) - Ū⃗ᵣ, Ū⃗ᵢ = vec(real(U_goal)), vec(imag(U_goal)) - Tᵣ = Ū⃗ᵣ' * U⃗ᵣ + Ū⃗ᵢ' * U⃗ᵢ - Tᵢ = Ū⃗ᵣ' * U⃗ᵢ - Ū⃗ᵢ' * U⃗ᵣ - Wᵣᵣ = Ū⃗ᵣ * Ū⃗ᵣ' - Wᵢᵢ = Ū⃗ᵢ * Ū⃗ᵢ' - Wᵣᵢ = Ū⃗ᵣ * Ū⃗ᵢ' - Wᵢᵣ = Wᵣᵢ' - ℱ = 1 / n * sqrt(Tᵣ^2 + Tᵢ^2) - ∇ᵣℱ = 1 / (n^2 * ℱ) * (Tᵣ * Ū⃗ᵣ - Tᵢ * Ū⃗ᵢ) - ∇ᵢℱ = 1 / (n^2 * ℱ) * (Tᵣ * Ū⃗ᵢ + Tᵢ * Ū⃗ᵣ) - ∂ᵣ²ℱ = 1 / ℱ * (-∇ᵣℱ * ∇ᵣℱ' + 1 / n^2 * (Wᵣᵣ + Wᵢᵢ)) - ∂ᵢ²ℱ = 1 / ℱ * (-∇ᵢℱ * ∇ᵢℱ' + 1 / n^2 * (Wᵣᵣ + Wᵢᵢ)) - ∂ᵣ∂ᵢℱ = 1 / ℱ * (-∇ᵢℱ * ∇ᵣℱ' + 1 / n^2 * (Wᵢᵣ - Wᵣᵢ)) - ∂²ℱ = [∂ᵣ²ℱ ∂ᵣ∂ᵢℱ; ∂ᵣ∂ᵢℱ' ∂ᵢ²ℱ] - permutation = vcat(vcat([[slice(j, n), slice(j, n) .+ n^2] for j = 1:n]...)...) - ∂²ℱ = ∂²ℱ[permutation, permutation] - return -sign(1 - ℱ) * ∂²ℱ -end - - - -struct UnitaryInfidelityLoss <: AbstractLoss - l::Function - ∇l::Function - ∇²l::Function - ∇²l_structure::Vector{Tuple{Int,Int}} - name::Symbol - - function UnitaryInfidelityLoss( - name::Symbol, - Ũ⃗_goal::AbstractVector; - subspace::Union{Nothing, AbstractVector{Int}, Tuple{AbstractVector{Int},AbstractVector{Int}}}=nothing, - ) - if isnothing(subspace) - subspace = axes(iso_vec_to_operator(Ũ⃗_goal)) - elseif subspace isa AbstractVector{Int} - subspace = (subspace, subspace) - end - - l = Ũ⃗ -> unitary_infidelity(Ũ⃗, Ũ⃗_goal, subspace) - - @views ∇l = Ũ⃗ -> begin - subspace_rows, subspace_cols = subspace - n_subspace = length(subspace_rows) - n_full = Int(sqrt(length(Ũ⃗) ÷ 2)) - ∇l_subspace = unitary_infidelity_gradient(Ũ⃗, Ũ⃗_goal, subspace) - ∇l_full = zeros(2 * n_full^2) - for j ∈ eachindex(subspace_cols) - ∇l_full[slice(2subspace_cols[j] - 1, subspace_rows, n_full)] = - ∇l_subspace[slice(2j - 1, n_subspace)] - ∇l_full[slice(2subspace_cols[j], subspace_rows, n_full)] = - ∇l_subspace[slice(2j, n_subspace)] - end - return ∇l_full - end - - @views ∇²l = Ũ⃗ -> begin - subspace_rows, subspace_cols = subspace - n_subspace = length(subspace_rows) - n_full = Int(sqrt(length(Ũ⃗) ÷ 2)) - ∇²l_subspace = unitary_infidelity_hessian(Ũ⃗, Ũ⃗_goal, subspace) - ∇²l_full = zeros(2 * n_full^2, 2 * n_full^2) - # TODO: make sure this is correct for case where subspace_rows ≠ subspace_cols - for k ∈ eachindex(subspace_cols) - for j ∈ eachindex(subspace_cols) - ∇²l_full[ - slice(2subspace_cols[k] - 1, subspace_rows, n_full), - slice(2subspace_cols[j] - 1, subspace_rows, n_full) - ] = ∇²l_subspace[ - slice(2k - 1, n_subspace), - slice(2j - 1, n_subspace) - ] - - ∇²l_full[ - slice(2subspace_cols[k] - 1, subspace_rows, n_full), - slice(2subspace_cols[j], subspace_rows, n_full) - ] = ∇²l_subspace[ - slice(2k - 1, n_subspace), - slice(2j, n_subspace) - ] - - ∇²l_full[ - slice(2subspace_cols[k], subspace_rows, n_full), - slice(2subspace_cols[j] - 1, subspace_rows, n_full) - ] = ∇²l_subspace[ - slice(2k, n_subspace), - slice(2j - 1, n_subspace) - ] - - ∇²l_full[ - slice(2subspace_cols[k], subspace_rows, n_full), - slice(2subspace_cols[j], subspace_rows, n_full) - ] = ∇²l_subspace[ - slice(2k, n_subspace), - slice(2j, n_subspace) - ] - end - end - return ∇²l_full - end - - Ũ⃗_dim = length(Ũ⃗_goal) - ∇²l_structure = [] - for (i, j) ∈ Iterators.product(1:Ũ⃗_dim, 1:Ũ⃗_dim) - if i ≤ j - push!(∇²l_structure, (i, j)) - end - end - return new(l, ∇l, ∇²l, ∇²l_structure, name) - end -end - -function (loss::UnitaryInfidelityLoss)( - Ũ⃗_end::AbstractVector{<:Real}; - gradient=false, - hessian=false -) - @assert !(gradient && hessian) - if !(gradient || hessian) - return loss.l(Ũ⃗_end) - elseif gradient - return loss.∇l(Ũ⃗_end) - elseif hessian - return loss.∇²l(Ũ⃗_end) - end -end - - - - - - - -function unitary_trace_loss(Ũ⃗::AbstractVector, Ũ⃗_goal::AbstractVector) - U = iso_vec_to_operator(Ũ⃗) - Ugoal = iso_vec_to_operator(Ũ⃗_goal) - return 1 / 2 * tr(sqrt((U - Ugoal)' * (U - Ugoal))) -end - - -struct Loss <: AbstractLoss - l::Function - ∇l::Function - ∇²l::Function - ∇²l_structure::Vector{Tuple{Int,Int}} - name::Symbol - - function Loss( - Z::NamedTrajectory, - J::Function, - x::Symbol - ) - @assert x ∈ Z.names - @assert Z.goal[x] isa AbstractVector - - x_goal = Z.goal[x] - - J = x̄ -> J(x̄, x_goal) - ∇J = x̄ -> ForwardDiff.gradient(J, x̄) - - Symbolics.@variables x̄[1:Z.dims[x]] - x̄ = collect(x̄) - - ∇²J_symbolic = Symbolics.sparsehessian(J(x̄), x̄) - rows, cols, _ = findnz(∇²J_symbolic) - rowcols = collect(zip(rows, cols)) - filter!((row, col) -> row ≥ col, rowcols) - ∇²J_structure = rowcols - - ∇²J_expression = Symbolics.build_function(∇²J_symbolic, x̄) - ∇²J = eval(∇²J_expression[1]) - - return new(J, ∇J, ∇²J, ∇²J_structure, x) - end -end - -function (loss::Loss)(Z::NamedTrajectory; gradient=false, hessian=false) - @assert !(gradient && hessian) - if !(gradient || hessian) - return loss.l(Z[end][loss.name]) - elseif gradient - return loss.∇l(Z[end][loss.name]) - elseif hessian - return loss.∇²l(Z[end][loss.name]) - end -end - - -struct UnitaryTraceLoss <: AbstractLoss - l::Function - ∇l::Function - ∇²l::Function - ∇²l_structure::Vector{Tuple{Int,Int}} - name::Symbol - - function UnitaryTraceLoss( - name::Symbol, - Ũ⃗_goal::AbstractVector - ) - l = Ũ⃗ -> unitary_trace_loss(Ũ⃗, Ũ⃗_goal) - ∇l = Ũ⃗ -> ForwardDiff.gradient(l, Ũ⃗) - ∇²l = Ũ⃗ -> ForwardDiff.hessian(l, Ũ⃗) - Ũ⃗_dim = length(Ũ⃗_goal) - ∇²l_structure = [] - for (i, j) ∈ Iterators.product(1:Ũ⃗_dim, 1:Ũ⃗_dim) - if i ≤ j - push!(∇²l_structure, (i, j)) - end - end - return new(l, ∇l, ∇²l, ∇²l_structure, name) - end -end - -function (loss::UnitaryTraceLoss)( - Ũ⃗_end::AbstractVector{<:Real}; - gradient=false, - hessian=false -) - @assert !(gradient && hessian) - - if !(gradient || hessian) - return loss.l(Ũ⃗_end) - elseif gradient - return loss.∇l(Ũ⃗_end) - elseif hessian - return loss.∇²l(Ũ⃗_end) - end -end - - - - - -struct InfidelityLoss <: AbstractLoss - l::Function - ∇l::Function - ∇²l::Function - ∇²l_structure::Vector{Tuple{Int,Int}} - wfn_name::Symbol - - function InfidelityLoss( - name::Symbol, - ψ̃_goal::AbstractVector - ) - l = ψ̃ -> infidelity(ψ̃, ψ̃_goal) - ∇l = ψ̃ -> ForwardDiff.gradient(l, ψ̃) - - Symbolics.@variables ψ̃[1:length(ψ̃_goal)] - ψ̃ = collect(ψ̃) - - ∇²l_symbolic = Symbolics.sparsehessian(l(ψ̃), ψ̃) - K, J, _ = findnz(∇²l_symbolic) - kjs = collect(zip(K, J)) - filter!(((k, j),) -> k ≤ j, kjs) - ∇²l_structure = kjs - - ∇²l_expression = Symbolics.build_function(∇²l_symbolic, ψ̃) - ∇²l = eval(∇²l_expression[1]) - - return new(l, ∇l, ∇²l, ∇²l_structure, name) - end -end - -function (loss::InfidelityLoss)( - ψ̃_end::AbstractVector{<:Real}; - gradient=false, - hessian=false -) - @assert !(gradient && hessian) - - if !(gradient || hessian) - return loss.l(ψ̃_end) - elseif gradient - return loss.∇l(ψ̃_end) - elseif hessian - return loss.∇²l(ψ̃_end) - end -end - - - - - - - - -struct QuantumLoss - cs::Vector{Function} - isodim::Int - - function QuantumLoss( - sys::AbstractQuantumSystem, - loss::Symbol = :infidelity_loss - ) - if loss == :energy_loss - cs = [ψ̃ⁱ -> eval(loss)(ψ̃ⁱ, sys.H_target) for i = 1:sys.nqstates] - elseif loss == :neg_entropy_loss - cs = [ψ̃ⁱ -> eval(loss)(ψ̃ⁱ) for i = 1:sys.nqstates] - else - cs = [ - ψ̃ⁱ -> eval(loss)( - ψ̃ⁱ, - sys.ψ̃goal[slice(i, sys.isodim)] - ) for i = 1:sys.nqstates - ] - end - return new(cs, sys.isodim) - end -end - -function (qloss::QuantumLoss)(ψ̃::AbstractVector) - loss = 0.0 - for (i, cⁱ) in enumerate(qloss.cs) - loss += cⁱ(ψ̃[slice(i, qloss.isodim)]) - end - return loss -end - -struct QuantumLossGradient - ∇cs::Vector{Function} - isodim::Int - - function QuantumLossGradient( - loss::QuantumLoss; - simplify=true - ) - Symbolics.@variables ψ̃[1:loss.isodim] - - ψ̃ = collect(ψ̃) - - ∇cs_symbs = [ - Symbolics.gradient(c(ψ̃), ψ̃; simplify=simplify) - for c in loss.cs - ] - - ∇cs_exprs = [ - Symbolics.build_function(∇c, ψ̃) - for ∇c in ∇cs_symbs - ] - - ∇cs = [ - eval(∇c_expr[1]) - for ∇c_expr in ∇cs_exprs - ] - - return new(∇cs, loss.isodim) - end -end - -@views function (∇c::QuantumLossGradient)( - ψ̃::AbstractVector -) - ∇ = similar(ψ̃) - - for (i, ∇cⁱ) in enumerate(∇c.∇cs) - - ψ̃ⁱ_slice = slice(i, ∇c.isodim) - - ∇[ψ̃ⁱ_slice] = ∇cⁱ(ψ̃[ψ̃ⁱ_slice]) - end - - return ∇ -end - -struct QuantumLossHessian - ∇²cs::Vector{Function} - ∇²c_structures::Vector{Vector{Tuple{Int, Int}}} - isodim::Int - - function QuantumLossHessian( - loss::QuantumLoss; - simplify=true - ) - - Symbolics.@variables ψ̃[1:loss.isodim] - ψ̃ = collect(ψ̃) - - ∇²c_symbs = [ - Symbolics.sparsehessian( - c(ψ̃), - ψ̃; - simplify=simplify - ) for c in loss.cs - ] - - ∇²c_structures = [] - - for ∇²c_symb in ∇²c_symbs - K, J, _ = findnz(∇²c_symb) - - KJ = collect(zip(K, J)) - - filter!(((k, j),) -> k ≤ j, KJ) - - push!(∇²c_structures, KJ) - end - - ∇²c_exprs = [ - Symbolics.build_function(∇²c_symb, ψ̃) - for ∇²c_symb in ∇²c_symbs - ] - - ∇²cs = [ - eval(∇²c_expr[1]) - for ∇²c_expr in ∇²c_exprs - ] - - return new(∇²cs, ∇²c_structures, loss.isodim) - end -end - -function StructureUtils.structure( - H::QuantumLossHessian, - T::Int, - vardim::Int -) - H_structure = [] - - T_offset = index(T, 0, vardim) - - for (i, KJⁱ) in enumerate(H.∇²c_structures) - - i_offset = index(i, 0, H.isodim) - - for kj in KJⁱ - push!(H_structure, (T_offset + i_offset) .+ kj) - end - end - - return H_structure -end - -@views function (H::QuantumLossHessian)(ψ̃::AbstractVector) - - Hs = [] - - for (i, ∇²cⁱ) in enumerate(H.∇²cs) - - ψ̃ⁱ = ψ̃[slice(i, H.isodim)] - - for (k, j) in H.∇²c_structures[i] - - Hⁱᵏʲ = ∇²cⁱ(ψ̃ⁱ)[k, j] - - append!(Hs, Hⁱᵏʲ) - end - end - - return Hs -end - - -# -# primary loss functions -# - - -function energy_loss( - ψ̃::AbstractVector, - H::AbstractMatrix -) - ψ = iso_to_ket(ψ̃) - return real(ψ' * H * ψ) -end - - -# TODO: figure out a way to implement this without erroring and Von Neumann entropy being always 0 for a pure state -function neg_entropy_loss( - ψ̃::AbstractVector -) - ψ = iso_to_ket(ψ̃) - ρ = ψ * ψ' - ρ = Hermitian(ρ) - return tr(ρ * log(ρ)) -end - - - - -# -# experimental loss functions -# - -function pure_real_loss(ψ̃, ψ̃goal) - ψ = iso_to_ket(ψ̃) - ψgoal = iso_to_ket(ψ̃goal) - return -(ψ'ψgoal) -end - -function geodesic_loss(ψ̃, ψ̃goal) - ψ = iso_to_ket(ψ̃) - ψgoal = iso_to_ket(ψ̃goal) - amp = ψ'ψgoal - return min(abs(1 - amp), abs(1 + amp)) -end - -function real_loss(ψ̃, ψ̃goal) - ψ = iso_to_ket(ψ̃) - ψgoal = iso_to_ket(ψ̃goal) - amp = ψ'ψgoal - return min(abs(1 - real(amp)), abs(1 + real(amp))) -end - -function iso_infidelity(ψ̃, ψ̃f) - ψ = iso_to_ket(ψ̃) - ψf = iso_to_ket(ψ̃f) - return 1 - abs2(ψ'ψf) -end - - -function quaternionic_loss(ψ̃, ψ̃goal) - return min( - abs(1 - dot(ψ̃, ψ̃goal)), - abs(1 + dot(ψ̃, ψ̃goal)) - ) - -end - -end diff --git a/src/losses/_experimental_loss_functions.jl b/src/losses/_experimental_loss_functions.jl new file mode 100644 index 00000000..e6de76e5 --- /dev/null +++ b/src/losses/_experimental_loss_functions.jl @@ -0,0 +1,51 @@ +# +# experimental loss functions +# +# TODO: renormalize vectors in place of abs +# ⋅ penalize loss to remain near unit norm +# ⋅ Σ α * (1 - ψ̃'ψ̃), α = 1e-3 + +function energy_loss( + ψ̃::AbstractVector, + H::AbstractMatrix +) + ψ = iso_to_ket(ψ̃) + return real(ψ' * H * ψ) +end + +# TODO: figure out a way to implement this without erroring and Von Neumann entropy being always 0 for a pure state +function neg_entropy_loss( + ψ̃::AbstractVector +) + ψ = iso_to_ket(ψ̃) + ρ = ψ * ψ' + ρ = Hermitian(ρ) + return tr(ρ * log(ρ)) +end + +function pure_real_loss(ψ̃, ψ̃goal) + ψ = iso_to_ket(ψ̃) + ψgoal = iso_to_ket(ψ̃goal) + return -(ψ'ψgoal) +end + +function geodesic_loss(ψ̃, ψ̃goal) + ψ = iso_to_ket(ψ̃) + ψgoal = iso_to_ket(ψ̃goal) + amp = ψ'ψgoal + return min(abs(1 - amp), abs(1 + amp)) +end + +function real_loss(ψ̃, ψ̃goal) + ψ = iso_to_ket(ψ̃) + ψgoal = iso_to_ket(ψ̃goal) + amp = ψ'ψgoal + return min(abs(1 - real(amp)), abs(1 + real(amp))) +end + +function quaternionic_loss(ψ̃, ψ̃goal) + return min( + abs(1 - dot(ψ̃, ψ̃goal)), + abs(1 + dot(ψ̃, ψ̃goal)) + ) +end \ No newline at end of file diff --git a/src/losses/_losses.jl b/src/losses/_losses.jl new file mode 100644 index 00000000..e31bdd43 --- /dev/null +++ b/src/losses/_losses.jl @@ -0,0 +1,86 @@ +module Losses + +export Loss + +using ..QuantumObjectUtils +using ..Isomorphisms +using ..QuantumSystems +using ..StructureUtils + +using NamedTrajectories +using TrajectoryIndexingUtils + +using LinearAlgebra +using SparseArrays +using ForwardDiff +using Symbolics +using TestItemRunner + +# TODO: +# - [ ] Do not reference the Z object in the loss (components only / remove "name") + +# ----------------------------------------------------------------------------- # +# Abstract Loss # +# ----------------------------------------------------------------------------- # + +abstract type AbstractLoss end + +include("_experimental_loss_functions.jl") +include("quantum_loss.jl") +include("quantum_state_infidelity_loss.jl") +include("unitary_trace_loss.jl") +include("unitary_infidelity_loss.jl") + +# ----------------------------------------------------------------------------- # +# Loss # +# ----------------------------------------------------------------------------- # + +struct Loss <: AbstractLoss + l::Function + ∇l::Function + ∇²l::Function + ∇²l_structure::Vector{Tuple{Int,Int}} + name::Symbol + + function Loss( + Z::NamedTrajectory, + J::Function, + x::Symbol + ) + @assert x ∈ Z.names + @assert Z.goal[x] isa AbstractVector + + x_goal = Z.goal[x] + + J = x̄ -> J(x̄, x_goal) + ∇J = x̄ -> ForwardDiff.gradient(J, x̄) + + Symbolics.@variables x̄[1:Z.dims[x]] + x̄ = collect(x̄) + + ∇²J_symbolic = Symbolics.sparsehessian(J(x̄), x̄) + rows, cols, _ = findnz(∇²J_symbolic) + rowcols = collect(zip(rows, cols)) + filter!((row, col) -> row ≥ col, rowcols) + ∇²J_structure = rowcols + + ∇²J_expression = Symbolics.build_function(∇²J_symbolic, x̄) + ∇²J = eval(∇²J_expression[1]) + + return new(J, ∇J, ∇²J, ∇²J_structure, x) + end +end + +function (loss::Loss)(Z::NamedTrajectory; gradient=false, hessian=false) + @assert !(gradient && hessian) + if !(gradient || hessian) + return loss.l(Z[end][loss.name]) + elseif gradient + return loss.∇l(Z[end][loss.name]) + elseif hessian + return loss.∇²l(Z[end][loss.name]) + end +end + + +end diff --git a/src/losses/quantum_loss.jl b/src/losses/quantum_loss.jl new file mode 100644 index 00000000..95283415 --- /dev/null +++ b/src/losses/quantum_loss.jl @@ -0,0 +1,169 @@ +export QuantumLoss +export QuantumLossGradient +export QuantumLossHessian + + +struct QuantumLoss + cs::Vector{Function} + isodim::Int + + function QuantumLoss( + sys::AbstractQuantumSystem, + loss::Symbol = :infidelity_loss + ) + if loss == :energy_loss + cs = [ψ̃ⁱ -> eval(loss)(ψ̃ⁱ, sys.H_target) for i = 1:sys.nqstates] + elseif loss == :neg_entropy_loss + cs = [ψ̃ⁱ -> eval(loss)(ψ̃ⁱ) for i = 1:sys.nqstates] + else + cs = [ + ψ̃ⁱ -> eval(loss)( + ψ̃ⁱ, + sys.ψ̃goal[slice(i, sys.isodim)] + ) for i = 1:sys.nqstates + ] + end + return new(cs, sys.isodim) + end +end + +function (qloss::QuantumLoss)(ψ̃::AbstractVector) + loss = 0.0 + for (i, cⁱ) in enumerate(qloss.cs) + loss += cⁱ(ψ̃[slice(i, qloss.isodim)]) + end + return loss +end + +struct QuantumLossGradient + ∇cs::Vector{Function} + isodim::Int + + function QuantumLossGradient( + loss::QuantumLoss; + simplify=true + ) + Symbolics.@variables ψ̃[1:loss.isodim] + + ψ̃ = collect(ψ̃) + + ∇cs_symbs = [ + Symbolics.gradient(c(ψ̃), ψ̃; simplify=simplify) + for c in loss.cs + ] + + ∇cs_exprs = [ + Symbolics.build_function(∇c, ψ̃) + for ∇c in ∇cs_symbs + ] + + ∇cs = [ + eval(∇c_expr[1]) + for ∇c_expr in ∇cs_exprs + ] + + return new(∇cs, loss.isodim) + end +end + +@views function (∇c::QuantumLossGradient)( + ψ̃::AbstractVector +) + ∇ = similar(ψ̃) + + for (i, ∇cⁱ) in enumerate(∇c.∇cs) + + ψ̃ⁱ_slice = slice(i, ∇c.isodim) + + ∇[ψ̃ⁱ_slice] = ∇cⁱ(ψ̃[ψ̃ⁱ_slice]) + end + + return ∇ +end + +struct QuantumLossHessian + ∇²cs::Vector{Function} + ∇²c_structures::Vector{Vector{Tuple{Int, Int}}} + isodim::Int + + function QuantumLossHessian( + loss::QuantumLoss; + simplify=true + ) + + Symbolics.@variables ψ̃[1:loss.isodim] + ψ̃ = collect(ψ̃) + + ∇²c_symbs = [ + Symbolics.sparsehessian( + c(ψ̃), + ψ̃; + simplify=simplify + ) for c in loss.cs + ] + + ∇²c_structures = [] + + for ∇²c_symb in ∇²c_symbs + K, J, _ = findnz(∇²c_symb) + + KJ = collect(zip(K, J)) + + filter!(((k, j),) -> k ≤ j, KJ) + + push!(∇²c_structures, KJ) + end + + ∇²c_exprs = [ + Symbolics.build_function(∇²c_symb, ψ̃) + for ∇²c_symb in ∇²c_symbs + ] + + ∇²cs = [ + eval(∇²c_expr[1]) + for ∇²c_expr in ∇²c_exprs + ] + + return new(∇²cs, ∇²c_structures, loss.isodim) + end +end + +function StructureUtils.structure( + H::QuantumLossHessian, + T::Int, + vardim::Int +) + H_structure = [] + + T_offset = index(T, 0, vardim) + + for (i, KJⁱ) in enumerate(H.∇²c_structures) + + i_offset = index(i, 0, H.isodim) + + for kj in KJⁱ + push!(H_structure, (T_offset + i_offset) .+ kj) + end + end + + return H_structure +end + +@views function (H::QuantumLossHessian)(ψ̃::AbstractVector) + + Hs = [] + + for (i, ∇²cⁱ) in enumerate(H.∇²cs) + + ψ̃ⁱ = ψ̃[slice(i, H.isodim)] + + for (k, j) in H.∇²c_structures[i] + + Hⁱᵏʲ = ∇²cⁱ(ψ̃ⁱ)[k, j] + + append!(Hs, Hⁱᵏʲ) + end + end + + return Hs +end \ No newline at end of file diff --git a/src/losses/quantum_state_infidelity_loss.jl b/src/losses/quantum_state_infidelity_loss.jl new file mode 100644 index 00000000..e89660df --- /dev/null +++ b/src/losses/quantum_state_infidelity_loss.jl @@ -0,0 +1,99 @@ +export fidelity +export iso_fidelity + +export InfidelityLoss + +### +### InfidelityLoss +### + +@doc raw""" + fidelity(ψ, ψ_goal) + +Calculate the fidelity between two quantum states `ψ` and `ψ_goal`. +""" +function fidelity( + ψ::AbstractVector, + ψ_goal::AbstractVector; + subspace::AbstractVector{Int}=1:length(ψ) +) + ψ = ψ[subspace] + ψ_goal = ψ_goal[subspace] + return abs2(ψ_goal' * ψ) +end + +@doc raw""" + iso_fidelity(ψ̃, ψ̃_goal) + +Calculate the fidelity between two quantum states in their isomorphic form `ψ̃` and `ψ̃_goal`. +""" +function iso_fidelity( + ψ̃::AbstractVector, + ψ̃_goal::AbstractVector; + subspace::AbstractVector{Int}=1:length(iso_to_ket(ψ̃)) +) + ψ = iso_to_ket(ψ̃) + ψ_goal = iso_to_ket(ψ̃_goal) + return fidelity(ψ, ψ_goal, subspace=subspace) +end + +""" + iso_infidelity(ψ̃, ψ̃goal) + +Returns the iso_infidelity between two quantum statevectors specified +in the ``\\mathbb{C}^n \\to \\mathbb{R}^{2n}`` isomorphism space. + +""" +function iso_infidelity( + ψ̃::AbstractVector, + ψ̃goal::AbstractVector, + subspace::AbstractVector{Int}=1:length(iso_to_ket(ψ̃)) +) + return abs(1 - iso_fidelity(ψ̃, ψ̃goal, subspace=subspace)) +end + +struct InfidelityLoss <: AbstractLoss + l::Function + ∇l::Function + ∇²l::Function + ∇²l_structure::Vector{Tuple{Int,Int}} + wfn_name::Symbol + + function InfidelityLoss( + name::Symbol, + ψ̃_goal::AbstractVector + ) + l = ψ̃ -> iso_infidelity(ψ̃, ψ̃_goal) + ∇l = ψ̃ -> ForwardDiff.gradient(l, ψ̃) + + Symbolics.@variables ψ̃[1:length(ψ̃_goal)] + ψ̃ = collect(ψ̃) + + ∇²l_symbolic = Symbolics.sparsehessian(l(ψ̃), ψ̃) + K, J, _ = findnz(∇²l_symbolic) + kjs = collect(zip(K, J)) + filter!(((k, j),) -> k ≤ j, kjs) + ∇²l_structure = kjs + + ∇²l_expression = Symbolics.build_function(∇²l_symbolic, ψ̃) + ∇²l = eval(∇²l_expression[1]) + + return new(l, ∇l, ∇²l, ∇²l_structure, name) + end +end + +function (loss::InfidelityLoss)( + ψ̃_end::AbstractVector{<:Real}; + gradient=false, + hessian=false +) + @assert !(gradient && hessian) + + if !(gradient || hessian) + return loss.l(ψ̃_end) + elseif gradient + return loss.∇l(ψ̃_end) + elseif hessian + return loss.∇²l(ψ̃_end) + end +end \ No newline at end of file diff --git a/src/losses/unitary_infidelity_loss.jl b/src/losses/unitary_infidelity_loss.jl new file mode 100644 index 00000000..fcf184b5 --- /dev/null +++ b/src/losses/unitary_infidelity_loss.jl @@ -0,0 +1,513 @@ +export unitary_fidelity +export iso_vec_unitary_fidelity +export unitary_free_phase_fidelity +export iso_vec_unitary_free_phase_fidelity + +export UnitaryInfidelityLoss +export UnitaryFreePhaseInfidelityLoss + +### +### UnitaryInfidelityLoss +### + +@doc raw""" + unitary_fidelity(U::Matrix, U_goal::Matrix; kwargs...) + unitary_fidelity(Ũ⃗::AbstractVector, Ũ⃗_goal::AbstractVector; kwargs...) + +Calculate the fidelity between two unitary operators `U` and `U_goal`. + +```math +\mathcal{F}(U, U_{\text{goal}}) = \frac{1}{n} \abs{\tr \qty(U_{\text{goal}}^\dagger U)} +``` + +where $n$ is the dimension of the unitary operators. + +# Keyword Arguments +- `subspace::AbstractVector{Int}`: The subspace to calculate the fidelity over. +""" +@views function unitary_fidelity( + U::Matrix, + U_goal::Matrix; + subspace::AbstractVector{Int}=axes(U_goal, 1) +) + U_goal = U_goal[subspace, subspace] + U = U[subspace, subspace] + return 1 / size(U_goal, 1) * abs(tr(U_goal'U)) +end + +@views function unitary_fidelity( + Ũ⃗::AbstractVector, + Ũ⃗_goal::AbstractVector; + kwargs... +) + return unitary_fidelity( + iso_vec_to_operator(Ũ⃗), + iso_vec_to_operator(Ũ⃗_goal); + kwargs... + ) +end + +@doc raw""" + iso_vec_unitary_fidelity(Ũ⃗::AbstractVector, Ũ⃗_goal::AbstractVector) + +Returns the fidelity between the isomorphic unitary vector $\vec{\widetilde{U}} \sim U \in SU(n)$ +and the isomorphic goal unitary vector $\vec{\widetilde{U}}_{\text{goal}}$. + +```math +\begin{aligned} +\mathcal{F}(\vec{\widetilde{U}}, \vec{\widetilde{U}}_{\text{goal}}) &= \frac{1}{n} \abs{\tr \qty(U_{\text{goal}}^\dagger U)} \\ +&= \frac{1}{n} \sqrt{T_R^{2} + T_I^{2}} +\end{aligned} +``` + +where $T_R = \langle \vec{\widetilde{U}}_{\text{goal}, R}, \vec{\widetilde{U}}_R \rangle + \langle \vec{\widetilde{U}}_{\text{goal}, I}, \vec{\widetilde{U}}_I \rangle$ and $T_I = \langle \vec{\widetilde{U}}_{\text{goal}, R}, \vec{\widetilde{U}}_I \rangle - \langle \vec{\widetilde{U}}_{\text{goal}, I}, \vec{\widetilde{U}}_R \rangle$. + +""" +@inline @views function iso_vec_unitary_fidelity( + Ũ⃗::AbstractVector, + Ũ⃗_goal::AbstractVector; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1) +) + U = iso_vec_to_operator(Ũ⃗)[subspace, subspace] + n = size(U, 1) + U_goal = iso_vec_to_operator(Ũ⃗_goal)[subspace, subspace] + U⃗ᵣ, U⃗ᵢ = vec(real(U)), vec(imag(U)) + Ū⃗ᵣ, Ū⃗ᵢ = vec(real(U_goal)), vec(imag(U_goal)) + Tᵣ = Ū⃗ᵣ' * U⃗ᵣ + Ū⃗ᵢ' * U⃗ᵢ + Tᵢ = Ū⃗ᵣ' * U⃗ᵢ - Ū⃗ᵢ' * U⃗ᵣ + return 1 / n * sqrt(Tᵣ^2 + Tᵢ^2) +end + +@views function unitary_infidelity( + Ũ⃗::AbstractVector, + Ũ⃗_goal::AbstractVector; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1) +) + ℱ = iso_vec_unitary_fidelity(Ũ⃗, Ũ⃗_goal, subspace=subspace) + return abs(1 - ℱ) +end + +@views function unitary_infidelity_gradient( + Ũ⃗::AbstractVector, + Ũ⃗_goal::AbstractVector; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1) +) + U = iso_vec_to_operator(Ũ⃗)[subspace, subspace] + n = size(U, 1) + U_goal = iso_vec_to_operator(Ũ⃗_goal)[subspace, subspace] + U⃗ᵣ, U⃗ᵢ = vec(real(U)), vec(imag(U)) + Ū⃗ᵣ, Ū⃗ᵢ = vec(real(U_goal)), vec(imag(U_goal)) + Tᵣ = Ū⃗ᵣ' * U⃗ᵣ + Ū⃗ᵢ' * U⃗ᵢ + Tᵢ = Ū⃗ᵣ' * U⃗ᵢ - Ū⃗ᵢ' * U⃗ᵣ + ℱ = 1 / n * sqrt(Tᵣ^2 + Tᵢ^2) + ∇ᵣℱ = 1 / (n^2 * ℱ) * (Tᵣ * Ū⃗ᵣ - Tᵢ * Ū⃗ᵢ) + ∇ᵢℱ = 1 / (n^2 * ℱ) * (Tᵣ * Ū⃗ᵢ + Tᵢ * Ū⃗ᵣ) + ∇ℱ = [∇ᵣℱ; ∇ᵢℱ] + permutation = vcat(vcat([[slice(j, n), slice(j, n) .+ n^2] for j = 1:n]...)...) + ∇ℱ = ∇ℱ[permutation] + return -sign(1 - ℱ) * ∇ℱ +end + +@views function unitary_infidelity_hessian( + Ũ⃗::AbstractVector, + Ũ⃗_goal::AbstractVector; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1) +) + U = iso_vec_to_operator(Ũ⃗)[subspace, subspace] + n = size(U, 1) + U_goal = iso_vec_to_operator(Ũ⃗_goal)[subspace, subspace] + U⃗ᵣ, U⃗ᵢ = vec(real(U)), vec(imag(U)) + Ū⃗ᵣ, Ū⃗ᵢ = vec(real(U_goal)), vec(imag(U_goal)) + Tᵣ = Ū⃗ᵣ' * U⃗ᵣ + Ū⃗ᵢ' * U⃗ᵢ + Tᵢ = Ū⃗ᵣ' * U⃗ᵢ - Ū⃗ᵢ' * U⃗ᵣ + Wᵣᵣ = Ū⃗ᵣ * Ū⃗ᵣ' + Wᵢᵢ = Ū⃗ᵢ * Ū⃗ᵢ' + Wᵣᵢ = Ū⃗ᵣ * Ū⃗ᵢ' + Wᵢᵣ = Wᵣᵢ' + ℱ = 1 / n * sqrt(Tᵣ^2 + Tᵢ^2) + ∇ᵣℱ = 1 / (n^2 * ℱ) * (Tᵣ * Ū⃗ᵣ - Tᵢ * Ū⃗ᵢ) + ∇ᵢℱ = 1 / (n^2 * ℱ) * (Tᵣ * Ū⃗ᵢ + Tᵢ * Ū⃗ᵣ) + ∂ᵣ²ℱ = 1 / ℱ * (-∇ᵣℱ * ∇ᵣℱ' + 1 / n^2 * (Wᵣᵣ + Wᵢᵢ)) + ∂ᵢ²ℱ = 1 / ℱ * (-∇ᵢℱ * ∇ᵢℱ' + 1 / n^2 * (Wᵣᵣ + Wᵢᵢ)) + ∂ᵣ∂ᵢℱ = 1 / ℱ * (-∇ᵢℱ * ∇ᵣℱ' + 1 / n^2 * (Wᵢᵣ - Wᵣᵢ)) + ∂²ℱ = [∂ᵣ²ℱ ∂ᵣ∂ᵢℱ; ∂ᵣ∂ᵢℱ' ∂ᵢ²ℱ] + permutation = vcat(vcat([[slice(j, n), slice(j, n) .+ n^2] for j = 1:n]...)...) + ∂²ℱ = ∂²ℱ[permutation, permutation] + return -sign(1 - ℱ) * ∂²ℱ +end + +struct UnitaryInfidelityLoss <: AbstractLoss + l::Function + ∇l::Function + ∇²l::Function + ∇²l_structure::Vector{Tuple{Int,Int}} + name::Symbol + + function UnitaryInfidelityLoss( + name::Symbol, + Ũ⃗_goal::AbstractVector; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1) + ) + l = Ũ⃗ -> unitary_infidelity(Ũ⃗, Ũ⃗_goal, subspace=subspace) + + @views ∇l = Ũ⃗ -> begin + subspace_rows, subspace_cols = (subspace, subspace) + n_subspace = length(subspace_rows) + n_full = Int(sqrt(length(Ũ⃗) ÷ 2)) + ∇l_subspace = unitary_infidelity_gradient(Ũ⃗, Ũ⃗_goal, subspace=subspace) + ∇l_full = zeros(2 * n_full^2) + for j ∈ eachindex(subspace_cols) + ∇l_full[slice(2subspace_cols[j] - 1, subspace_rows, n_full)] = + ∇l_subspace[slice(2j - 1, n_subspace)] + ∇l_full[slice(2subspace_cols[j], subspace_rows, n_full)] = + ∇l_subspace[slice(2j, n_subspace)] + end + return ∇l_full + end + + @views ∇²l = Ũ⃗ -> begin + subspace_rows, subspace_cols = (subspace, subspace) + n_subspace = length(subspace_rows) + n_full = Int(sqrt(length(Ũ⃗) ÷ 2)) + ∇²l_subspace = unitary_infidelity_hessian(Ũ⃗, Ũ⃗_goal, subspace=subspace) + ∇²l_full = zeros(2 * n_full^2, 2 * n_full^2) + # NOTE: Assumes subspace_rows = subspace_cols + for k ∈ eachindex(subspace_cols) + for j ∈ eachindex(subspace_cols) + ∇²l_full[ + slice(2subspace_cols[k] - 1, subspace_rows, n_full), + slice(2subspace_cols[j] - 1, subspace_rows, n_full) + ] = ∇²l_subspace[ + slice(2k - 1, n_subspace), + slice(2j - 1, n_subspace) + ] + + ∇²l_full[ + slice(2subspace_cols[k] - 1, subspace_rows, n_full), + slice(2subspace_cols[j], subspace_rows, n_full) + ] = ∇²l_subspace[ + slice(2k - 1, n_subspace), + slice(2j, n_subspace) + ] + + ∇²l_full[ + slice(2subspace_cols[k], subspace_rows, n_full), + slice(2subspace_cols[j] - 1, subspace_rows, n_full) + ] = ∇²l_subspace[ + slice(2k, n_subspace), + slice(2j - 1, n_subspace) + ] + + ∇²l_full[ + slice(2subspace_cols[k], subspace_rows, n_full), + slice(2subspace_cols[j], subspace_rows, n_full) + ] = ∇²l_subspace[ + slice(2k, n_subspace), + slice(2j, n_subspace) + ] + end + end + return ∇²l_full + end + + Ũ⃗_dim = length(Ũ⃗_goal) + ∇²l_structure = [] + for (i, j) ∈ Iterators.product(1:Ũ⃗_dim, 1:Ũ⃗_dim) + if i ≤ j + push!(∇²l_structure, (i, j)) + end + end + return new(l, ∇l, ∇²l, ∇²l_structure, name) + end +end + +function (loss::UnitaryInfidelityLoss)( + Ũ⃗_end::AbstractVector{<:Real}; + gradient=false, + hessian=false +) + @assert !(gradient && hessian) + if !(gradient || hessian) + return loss.l(Ũ⃗_end) + elseif gradient + return loss.∇l(Ũ⃗_end) + elseif hessian + return loss.∇²l(Ũ⃗_end) + end +end + +### +### UnitaryFreePhaseInfidelityLoss +### + +function free_phase( + ϕs::AbstractVector, + Hs::AbstractVector{<:AbstractMatrix} +) + # NOTE: switch to expv for ForwardDiff + return reduce(kron, [exp(im * ϕ * H) for (ϕ, H) ∈ zip(ϕs, Hs)]) +end + +function free_phase_gradient( + ϕs::AbstractVector, + Hs::AbstractVector{<:AbstractMatrix} +) + R = free_phase(ϕs, Hs) + result = [zeros(eltype(R), size(R)) for _ in eachindex(ϕs)] + + # store identity matrices + identities = [Matrix{eltype(H)}(I, size(H)) for H in Hs] + + for (i, H) in enumerate(Hs) + # insert H into identities + identities[i] .= H + result[i] .= im * reduce(kron, identities) * R + # reset identities + identities[i] .= Matrix{eltype(H)}(I, size(H)) + end + return result +end + +@views function unitary_free_phase_fidelity( + U::Matrix, + U_goal::Matrix, + phases::AbstractVector, + phase_operators::AbstractVector{<:AbstractMatrix}; + subspace::AbstractVector{Int}=axes(U_goal, 1) +) + # extract phase rotation (assume phase operators span goal subspace) + R = zeros(eltype(U), size(U)) + R[subspace, subspace] = free_phase(phases, phase_operators) + + # calculate fidelity + return unitary_fidelity(R * U, U_goal, subspace=subspace) +end + +@views function iso_vec_unitary_free_phase_fidelity( + Ũ⃗::AbstractVector, + Ũ⃗_goal::AbstractVector, + phases::AbstractVector, + phase_operators::AbstractVector{<:AbstractMatrix}; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1) +) + U = iso_vec_to_operator(Ũ⃗) + U_goal = iso_vec_to_operator(Ũ⃗_goal) + return unitary_free_phase_fidelity(U, U_goal, phases, phase_operators, subspace=subspace) +end + +@views function iso_vec_unitary_free_phase_infidelity( + Ũ⃗::AbstractVector, + Ũ⃗_goal::AbstractVector, + phases::AbstractVector, + phase_operators::AbstractVector{<:AbstractMatrix}; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1) +) + return 1 - iso_vec_unitary_free_phase_fidelity(Ũ⃗, Ũ⃗_goal, phases, phase_operators, subspace=subspace) +end + +@views function iso_vec_unitary_free_phase_infidelity_gradient( + Ũ⃗::AbstractVector, + Ũ⃗_goal::AbstractVector, + phases::AbstractVector, + phase_operators::AbstractVector{<:AbstractMatrix}; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1) +) + n_phases = length(phases) + n_subspace = 2 * length(subspace) * length(subspace) + ∂ = spzeros(n_subspace + n_phases) + + # extract full state + U = iso_vec_to_operator(Ũ⃗) + + # extract full phase rotation + R = zeros(eltype(U), size(U)) + R[subspace, subspace] = free_phase(phases, phase_operators) + + # loss gradient in subspace + ∂ℱ_∂Ũ⃗_subspace = unitary_infidelity_gradient(operator_to_iso_vec(R * U), Ũ⃗_goal, subspace=subspace) + + # state slice in subspace + ∂[1:n_subspace] = operator_to_iso_vec(R[subspace, subspace]'iso_vec_to_operator(∂ℱ_∂Ũ⃗_subspace)) + + # phase slice + ∂[n_subspace .+ (1:n_phases)] = [ + ∂ℱ_∂Ũ⃗_subspace'operator_to_iso_vec(∂R_∂ϕⱼ_subspace * U[subspace, subspace]) + for ∂R_∂ϕⱼ_subspace in free_phase_gradient(phases, phase_operators) + ] + + return ∂ +end + +struct UnitaryFreePhaseInfidelityLoss <: AbstractLoss + l::Function + ∇l::Function + ∇²l::Function + ∇²l_structure::Vector{Tuple{Int,Int}} + name::Symbol + + function UnitaryFreePhaseInfidelityLoss( + Ũ⃗_goal::AbstractVector, + phase_operators::AbstractVector{<:AbstractMatrix}; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1), + ) + @assert reduce(.*, size.(phase_operators)) == length.(subspace) "phase operators must span the subspace" + + @views function l(Ũ⃗::AbstractVector, ϕ⃗::AbstractVector) + return iso_vec_unitary_free_phase_infidelity(Ũ⃗, Ũ⃗_goal, ϕ⃗, phase_operators, subspace=subspace) + end + + @views function ∇l(Ũ⃗::AbstractVector, ϕ⃗::AbstractVector) + subspace_rows, subspace_cols = subspace + n_phase = length(ϕ⃗) + n_rows = length(subspace_rows) + n_cols = length(subspace_cols) + n_full = Int(sqrt(length(Ũ⃗) ÷ 2)) + + # gradient in subspace + ∇l_subspace = iso_vec_unitary_free_phase_infidelity_gradient( + Ũ⃗, Ũ⃗_goal, ϕ⃗, phase_operators, subspace=subspace + ) + ∇l_full = zeros(2 * n_full^2 + n_phase) + + # state slice + for j ∈ 1:n_cols + ∇l_full[slice(2subspace_cols[j] - 1, subspace_rows, n_full)] = + ∇l_subspace[slice(2j - 1, n_rows)] + ∇l_full[slice(2subspace_cols[j], subspace_rows, n_full)] = + ∇l_subspace[slice(2j, n_rows)] + end + + # phase slice + ∇l_full[2 * n_full^2 .+ (1:n_phase)] = ∇l_subspace[2 * n_rows * n_cols .+ (1:n_phase)] + + return ∇l_full + end + + # TODO: implement analytic hessian + ∇²l(Ũ⃗, ϕ⃗) = [] + ∇²l_structure = [] + + return new(l, ∇l, ∇²l, ∇²l_structure, :NA) + end +end + +function (loss::UnitaryFreePhaseInfidelityLoss)( + Ũ⃗_end::AbstractVector{<:Real}, + ϕ⃗::AbstractVector{<:Real}; + gradient=false, + hessian=false +) + @assert !(gradient && hessian) + if !(gradient || hessian) + return loss.l(Ũ⃗_end, ϕ⃗) + elseif gradient + return loss.∇l(Ũ⃗_end, ϕ⃗) + elseif hessian + return loss.∇²l(Ũ⃗_end, ϕ⃗) + end +end + +# ============================================================================= # + +@testitem "Unitary fidelity" begin + X = [0 1; 1 0] + X = [0 1; 1 0] + Y = [0 -im; im 0] + @test unitary_fidelity(X, X) ≈ 1 + @test unitary_fidelity(X, Y) ≈ 0 +end + +@testitem "Isovec Unitary Fidelity" begin + using LinearAlgebra + + U_X = [0 1; 1 0] + U_Y = [0 -im; im 0] + + for U in [U_X, U_Y] + @test U'U ≈ I + end + + Ũ⃗_X = operator_to_iso_vec(U_X) + Ũ⃗_Y = operator_to_iso_vec(U_Y) + + # Test gate fidelity + @test iso_vec_unitary_fidelity(Ũ⃗_X, Ũ⃗_X) ≈ 1 + @test iso_vec_unitary_fidelity(Ũ⃗_X, Ũ⃗_Y) ≈ 0 + + + # Test asymmetric fidelity + U_fn(λ, φ) = [1 -exp(im * λ); exp(im * φ) exp(im * (φ + λ))] / sqrt(2) + U_1 = U_fn(π/4, π/3) + U_2 = U_fn(1.5, .33) + + for U in [U_1, U_2] + @test U'U ≈ I + end + + Ũ⃗_1 = operator_to_iso_vec(U_1) + Ũ⃗_2 = operator_to_iso_vec(U_2) + + @test iso_vec_unitary_fidelity(Ũ⃗_1, Ũ⃗_1) ≈ 1 + @test iso_vec_unitary_fidelity(Ũ⃗_2, Ũ⃗_2) ≈ 1 + @test iso_vec_unitary_fidelity(Ũ⃗_1, Ũ⃗_2) ≈ iso_vec_unitary_fidelity(Ũ⃗_2, Ũ⃗_1) + @test iso_vec_unitary_fidelity(Ũ⃗_1, Ũ⃗_2) ≈ abs(tr(U_1'U_2)) / 2 + + + # Test random fidelity + U_H1 = haar_random(2) + U_H2 = haar_random(2) + + for U in [U_H1, U_H2] + @test U'U ≈ I + end + + Ũ⃗_H1 = operator_to_iso_vec(U_H1) + Ũ⃗_H2 = operator_to_iso_vec(U_H2) + + @test iso_vec_unitary_fidelity(Ũ⃗_H1, Ũ⃗_H1) ≈ 1 + @test iso_vec_unitary_fidelity(Ũ⃗_H2, Ũ⃗_H2) ≈ 1 + @test iso_vec_unitary_fidelity(Ũ⃗_H1, Ũ⃗_X) ≈ abs(tr(U_H1'U_X)) / 2 + @test iso_vec_unitary_fidelity(Ũ⃗_H1, Ũ⃗_H2) ≈ abs(tr(U_H1'U_H2)) / 2 +end + + +@testitem "Isovec Unitary Fidelity Subspace" begin + using LinearAlgebra + + function test_iso_vec_unitary_fidelity( + U₁::AbstractMatrix, + U₂::AbstractMatrix, + subspace + ) + Ũ⃗₁ = operator_to_iso_vec(U₁) + Ũ⃗₂ = operator_to_iso_vec(U₂) + return iso_vec_unitary_fidelity(Ũ⃗₁, Ũ⃗₂, subspace=subspace) + end + + # Test random fidelity + test_subspaces = [ + get_subspace_indices([1:2, 1:1], [2, 2]), + get_subspace_indices([1:2, 2:2], [2, 2]), + ] + + for ii in test_subspaces + U_H1 = kron(haar_random(2), haar_random(2)) + U_H1_sub = U_H1[ii, ii] + U_H2 = kron(haar_random(2), haar_random(2)) + U_H2_sub = U_H2[ii, ii] + + # NOTE: subspace may not be unitary (?) + for U in [U_H1, U_H2] + @test U'U ≈ I + end + + fid = test_iso_vec_unitary_fidelity(U_H1, U_H2, ii) + fid_sub = test_iso_vec_unitary_fidelity(U_H1_sub, U_H2_sub, axes(U_H1_sub, 1)) + @test fid ≈ fid_sub + end +end + + +@testitem "Isovec Unitary Fidelity Gradient" begin + +end diff --git a/src/losses/unitary_trace_loss.jl b/src/losses/unitary_trace_loss.jl new file mode 100644 index 00000000..f8ef8d54 --- /dev/null +++ b/src/losses/unitary_trace_loss.jl @@ -0,0 +1,49 @@ +export UnitaryTraceLoss + + +function unitary_trace_loss(Ũ⃗::AbstractVector, Ũ⃗_goal::AbstractVector) + U = iso_vec_to_operator(Ũ⃗) + Ugoal = iso_vec_to_operator(Ũ⃗_goal) + return 1 / 2 * tr(sqrt((U - Ugoal)' * (U - Ugoal))) +end + +struct UnitaryTraceLoss <: AbstractLoss + l::Function + ∇l::Function + ∇²l::Function + ∇²l_structure::Vector{Tuple{Int,Int}} + name::Symbol + + function UnitaryTraceLoss( + name::Symbol, + Ũ⃗_goal::AbstractVector + ) + l = Ũ⃗ -> unitary_trace_loss(Ũ⃗, Ũ⃗_goal) + ∇l = Ũ⃗ -> ForwardDiff.gradient(l, Ũ⃗) + ∇²l = Ũ⃗ -> ForwardDiff.hessian(l, Ũ⃗) + Ũ⃗_dim = length(Ũ⃗_goal) + ∇²l_structure = [] + for (i, j) ∈ Iterators.product(1:Ũ⃗_dim, 1:Ũ⃗_dim) + if i ≤ j + push!(∇²l_structure, (i, j)) + end + end + return new(l, ∇l, ∇²l, ∇²l_structure, name) + end +end + +function (loss::UnitaryTraceLoss)( + Ũ⃗_end::AbstractVector{<:Real}; + gradient=false, + hessian=false +) + @assert !(gradient && hessian) + + if !(gradient || hessian) + return loss.l(Ũ⃗_end) + elseif gradient + return loss.∇l(Ũ⃗_end) + elseif hessian + return loss.∇²l(Ũ⃗_end) + end +end \ No newline at end of file diff --git a/src/objectives.jl b/src/objectives.jl deleted file mode 100644 index 906069f8..00000000 --- a/src/objectives.jl +++ /dev/null @@ -1,1181 +0,0 @@ -module Objectives - -export Objective - -export NullObjective -export QuantumObjective -export QuantumStateObjective -export QuantumUnitaryObjective -export UnitaryInfidelityObjective - -export MinimumTimeObjective -export InfidelityRobustnessObjective -export PairwiseInfidelityRobustnessObjective -export QuadraticRegularizer -export PairwiseQuadraticRegularizer -export QuadraticSmoothnessRegularizer -export L1Regularizer -export L1Regularizer! - -using TrajectoryIndexingUtils -using ..QuantumUtils -using ..QuantumSystems -using ..EmbeddedOperators -using ..Losses -using ..Constraints - -using NamedTrajectories -using LinearAlgebra -using SparseArrays -using Symbolics - -# -# objective functions -# - -""" - Objective - -A structure for defining objective functions. - -Fields: - `L`: the objective function - `∇L`: the gradient of the objective function - `∂²L`: the Hessian of the objective function - `∂²L_structure`: the structure of the Hessian of the objective function - `terms`: a vector of dictionaries containing the terms of the objective function -""" -struct Objective - L::Function - ∇L::Function - ∂²L::Union{Function, Nothing} - ∂²L_structure::Union{Function, Nothing} - terms::Vector{Dict} -end - -function NullObjective() - params = Dict(:type => :NullObjective) - L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) = 0.0 - ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) = zeros(Z.dim * Z.T) - ∂²L_structure(Z::NamedTrajectory) = [] - function ∂²L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory; return_moi_vals=true) - return return_moi_vals ? [] : spzeros(Z.dim * Z.T, Z.dim * Z.T) - end - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - -function Base.:+(obj1::Objective, obj2::Objective) - L = (Z⃗, Z) -> obj1.L(Z⃗, Z) + obj2.L(Z⃗, Z) - ∇L = (Z⃗, Z) -> obj1.∇L(Z⃗, Z) + obj2.∇L(Z⃗, Z) - if isnothing(obj1.∂²L) && isnothing(obj2.∂²L) - ∂²L = Nothing - ∂²L_structure = Nothing - elseif isnothing(obj1.∂²L) - ∂²L = (Z⃗, Z) -> obj2.∂²L(Z⃗, Z) - ∂²L_structure = obj2.∂²L_structure - elseif isnothing(obj2.∂²L) - ∂²L = (Z⃗, Z) -> obj1.∂²L(Z⃗, Z) - ∂²L_structure = obj1.∂²L_structure - else - ∂²L = (Z⃗, Z) -> vcat(obj1.∂²L(Z⃗, Z), obj2.∂²L(Z⃗, Z)) - ∂²L_structure = Z -> vcat(obj1.∂²L_structure(Z), obj2.∂²L_structure(Z)) - end - terms = vcat(obj1.terms, obj2.terms) - return Objective(L, ∇L, ∂²L, ∂²L_structure, terms) -end - -Base.:+(obj::Objective, ::Nothing) = obj -Base.:+(obj::Objective) = obj - -function Objective(terms::AbstractVector{<:Dict}) - return +(Objective.(terms)...) -end - -function Base.:*(num::Real, obj::Objective) - L = (Z⃗, Z) -> num * obj.L(Z⃗, Z) - ∇L = (Z⃗, Z) -> num * obj.∇L(Z⃗, Z) - if isnothing(obj.∂²L) - ∂²L = nothing - ∂²L_structure = nothing - else - ∂²L = (Z⃗, Z) -> num * obj.∂²L(Z⃗, Z) - ∂²L_structure = obj.∂²L_structure - end - return Objective(L, ∇L, ∂²L, ∂²L_structure, obj.terms) -end - -Base.:*(obj::Objective, num::Real) = num * obj - -function Objective(term::Dict) - return eval(term[:type])(; delete!(term, :type)...) -end - -# function to convert sparse matrix to tuple of vector of nonzero indices and vector of nonzero values -function sparse_to_moi(A::SparseMatrixCSC) - inds = collect(zip(findnz(A)...)) - vals = [A[i,j] for (i,j) ∈ inds] - return (inds, vals) -end -""" - QuantumObjective - - -""" -function QuantumObjective(; - names::Union{Nothing,Tuple{Vararg{Symbol}}}=nothing, - name::Union{Nothing,Symbol}=nothing, - goals::Union{Nothing,AbstractVector{<:Real},Tuple{Vararg{AbstractVector{<:Real}}}}=nothing, - loss::Symbol=:InfidelityLoss, - Q::Union{Float64, Vector{Float64}}=100.0, - eval_hessian::Bool=true -) - @assert !(isnothing(names) && isnothing(name)) "name or names must be specified" - @assert !isnothing(goals) "goals corresponding to names must be specified" - - if isnothing(names) - names = (name,) - end - - if goals isa AbstractVector - goals = (goals,) - end - - if Q isa Float64 - Q = ones(length(names)) * Q - else - @assert length(Q) == length(names) - end - - params = Dict( - :type => :QuantumObjective, - :names => names, - :goals => goals, - :loss => loss, - :Q => Q, - :eval_hessian => eval_hessian, - ) - - losses = [eval(loss)(name, goal) for (name, goal) ∈ zip(names, goals)] - - @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - loss = 0.0 - for (Qᵢ, lᵢ, name) ∈ zip(Q, losses, names) - name_slice = slice(Z.T, Z.components[name], Z.dim) - loss += Qᵢ * lᵢ(Z⃗[name_slice]) - end - return loss - end - - @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - ∇ = zeros(Z.dim * Z.T) - for (Qᵢ, lᵢ, name) ∈ zip(Q, losses, names) - name_slice = slice(Z.T, Z.components[name], Z.dim) - ∇[name_slice] = Qᵢ * lᵢ(Z⃗[name_slice]; gradient=true) - end - return ∇ - end - - function ∂²L_structure(Z::NamedTrajectory) - structure = [] - final_time_offset = index(Z.T, 0, Z.dim) - for (name, loss) ∈ zip(names, losses) - comp_start_offset = Z.components[name][1] - 1 - comp_hessian_structure = [ - ij .+ (final_time_offset + comp_start_offset) - for ij ∈ loss.∇²l_structure - ] - append!(structure, comp_hessian_structure) - end - return structure - end - - - @views function ∂²L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory; return_moi_vals=true) - H = spzeros(Z.dim * Z.T, Z.dim * Z.T) - for (Qᵢ, name, lᵢ) ∈ zip(Q, names, losses) - name_slice = slice(Z.T, Z.components[name], Z.dim) - H[name_slice, name_slice] = - Qᵢ * lᵢ(Z⃗[name_slice]; hessian=true) - end - if return_moi_vals - Hs = [H[i,j] for (i, j) ∈ ∂²L_structure(Z)] - return Hs - else - return H - end - end - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - - - -""" - UnitaryInfidelityObjective - - -""" -function UnitaryInfidelityObjective(; - name::Union{Nothing,Symbol}=nothing, - goal::Union{Nothing,AbstractVector{<:Real}}=nothing, - Q::Float64=100.0, - eval_hessian::Bool=true, - subspace=nothing -) - @assert !isnothing(goal) "unitary goal name must be specified" - - loss = :UnitaryInfidelityLoss - l = eval(loss)(name, goal; subspace=subspace) - - params = Dict( - :type => :UnitaryInfidelityObjective, - :name => name, - :goal => goal, - :Q => Q, - :eval_hessian => eval_hessian, - :subspace => subspace - ) - - @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - return Q * l(Z⃗[slice(Z.T, Z.components[name], Z.dim)]) - end - - @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - ∇ = zeros(Z.dim * Z.T) - Ũ⃗_slice = slice(Z.T, Z.components[name], Z.dim) - Ũ⃗ = Z⃗[Ũ⃗_slice] - ∇l = l(Ũ⃗; gradient=true) - ∇[Ũ⃗_slice] = Q * ∇l - return ∇ - end - - function ∂²L_structure(Z::NamedTrajectory) - final_time_offset = index(Z.T, 0, Z.dim) - comp_start_offset = Z.components[name][1] - 1 - structure = [ - ij .+ (final_time_offset + comp_start_offset) - for ij ∈ l.∇²l_structure - ] - return structure - end - - - @views function ∂²L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory; return_moi_vals=true) - H = spzeros(Z.dim * Z.T, Z.dim * Z.T) - Ũ⃗_slice = slice(Z.T, Z.components[name], Z.dim) - H[Ũ⃗_slice, Ũ⃗_slice] = Q * l(Z⃗[Ũ⃗_slice]; hessian=true) - if return_moi_vals - Hs = [H[i,j] for (i, j) ∈ ∂²L_structure(Z)] - return Hs - else - return H - end - end - - - # ∂²L_structure(Z::NamedTrajectory) = [] - - # ∂²L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) = [] - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - -function QuantumObjective( - name::Symbol, - traj::NamedTrajectory, - loss::Symbol, - Q::Float64 -) - goal = traj.goal[name] - return QuantumObjective(name=name, goals=goal, loss=loss, Q=Q) -end - -function UnitaryInfidelityObjective( - name::Symbol, - traj::NamedTrajectory, - Q::Float64; - subspace=nothing, - eval_hessian::Bool=true -) - return UnitaryInfidelityObjective(name=name, goal=traj.goal[name], Q=Q, subspace=subspace, eval_hessian=eval_hessian) -end - -function QuantumObjective( - names::Tuple{Vararg{Symbol}}, - traj::NamedTrajectory, - loss::Symbol, - Q::Float64 -) - goals = Tuple(traj.goal[name] for name in names) - return QuantumObjective(names=names, goals=goals, loss=loss, Q=Q) -end - -function QuantumUnitaryObjective( - name::Symbol, - traj::NamedTrajectory, - Q::Float64 -) - return QuantumObjective(name, traj, :UnitaryInfidelityLoss, Q) -end - -function QuantumStateObjective( - name::Symbol, - traj::NamedTrajectory, - Q::Float64 -) - return QuantumObjective(name, traj, :InfidelityLoss, Q) -end - -function QuadraticRegularizer(; - name::Union{Nothing, Symbol}=nothing, - times::Union{Nothing, AbstractVector{Int}}=nothing, - dim::Union{Nothing, Int}=nothing, - R::Union{Nothing, AbstractVector{<:Real}}=nothing, - baseline::Union{Nothing, AbstractArray{<:Real}}=nothing, - eval_hessian::Bool=true, - timestep_symbol::Symbol=:Δt -) - - @assert !isnothing(name) "name must be specified" - @assert !isnothing(times) "times must be specified" - @assert !isnothing(dim) "dim must be specified" - @assert !isnothing(R) "R must be specified" - if isnothing(baseline) - baseline = zeros(length(R), length(times)) - else - if size(baseline) != (length(R), length(times)) - throw(ArgumentError("size(baseline)=$(size(baseline)) must match $(length(R)) x $(length(times))")) - end - end - - params = Dict( - :type => :QuadraticRegularizer, - :name => name, - :times => times, - :dim => dim, - :baseline => baseline, - :R => R, - :eval_hessian => eval_hessian - ) - - @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - J = 0.0 - for t ∈ times - if Z.timestep isa Symbol - Δt = Z⃗[slice(t, Z.components[timestep_symbol], Z.dim)] - else - Δt = Z.timestep - end - - vₜ = Z⃗[slice(t, Z.components[name], Z.dim)] - Δv = vₜ .- baseline[:, t] - - rₜ = Δt .* Δv - J += 0.5 * rₜ' * (R .* rₜ) - end - return J - end - - @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - ∇ = zeros(Z.dim * Z.T) - Threads.@threads for t ∈ times - vₜ_slice = slice(t, Z.components[name], Z.dim) - Δv = Z⃗[vₜ_slice] .- baseline[:, t] - - if Z.timestep isa Symbol - Δt_slice = slice(t, Z.components[timestep_symbol], Z.dim) - Δt = Z⃗[Δt_slice] - ∇[Δt_slice] .= Δv' * (R .* (Δt .* Δv)) - else - Δt = Z.timestep - end - - ∇[vₜ_slice] .= R .* (Δt.^2 .* Δv) - end - return ∇ - end - - ∂²L = nothing - ∂²L_structure = nothing - - if eval_hessian - - ∂²L_structure = Z -> begin - structure = [] - # Hessian structure (eq. 17) - for t ∈ times - vₜ_slice = slice(t, Z.components[name], Z.dim) - vₜ_vₜ_inds = collect(zip(vₜ_slice, vₜ_slice)) - append!(structure, vₜ_vₜ_inds) - - if Z.timestep isa Symbol - Δt_slice = slice(t, Z.components[timestep_symbol], Z.dim) - # ∂²_vₜ_Δt - vₜ_Δt_inds = [(i, j) for i ∈ vₜ_slice for j ∈ Δt_slice] - append!(structure, vₜ_Δt_inds) - # ∂²_Δt_vₜ - Δt_vₜ_inds = [(i, j) for i ∈ Δt_slice for j ∈ vₜ_slice] - append!(structure, Δt_vₜ_inds) - # ∂²_Δt_Δt - Δt_Δt_inds = collect(zip(Δt_slice, Δt_slice)) - append!(structure, Δt_Δt_inds) - end - end - return structure - end - - ∂²L = (Z⃗, Z) -> begin - values = [] - # Match Hessian structure indices - for t ∈ times - if Z.timestep isa Symbol - Δt = Z⃗[slice(t, Z.components[timestep_symbol], Z.dim)] - append!(values, R .* Δt.^2) - # ∂²_vₜ_Δt, ∂²_Δt_vₜ - vₜ = Z⃗[slice(t, Z.components[name], Z.dim)] - Δv = vₜ .- baseline[:, t] - append!(values, 2 * (R .* (Δt .* Δv))) - append!(values, 2 * (R .* (Δt .* Δv))) - # ∂²_Δt_Δt - append!(values, Δv' * (R .* Δv)) - else - Δt = Z.timestep - append!(values, R .* Δt.^2) - end - end - return values - end - end - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - -function QuadraticRegularizer( - name::Symbol, - traj::NamedTrajectory, - R::AbstractVector{<:Real}; - kwargs... -) - return QuadraticRegularizer(; - name=name, - times=1:traj.T, - dim=traj.dim, - R=R, - kwargs... - ) -end - -function QuadraticRegularizer( - name::Symbol, - traj::NamedTrajectory, - R::Real; - kwargs... -) - return QuadraticRegularizer(; - name=name, - times=1:traj.T, - dim=traj.dim, - R=R * ones(traj.dims[name]), - kwargs... - ) -end - -@doc raw""" - PairwiseQuadraticRegularizer( - Q::AbstractVector{<:Real}, - times::AbstractVector{Int}, - name1::Symbol, - name2::Symbol; - timestep_symbol::Symbol=:Δt, - eval_hessian::Bool=false, - ) - -Create a pairwise quadratic regularizer for the trajectory component `name` with -regularization strength `Q`. The regularizer is defined as - -```math - J_{Ũ⃗}(u) = \sum_t \frac{1}{2} \Delta t_t^2 (Ũ⃗_{1,t} - Ũ⃗_{2,t})^T Q (Ũ⃗_{1,t} - Ũ⃗_{2,t}) -``` - -where $Ũ⃗_{1}$ and $Ũ⃗_{2}$ are selected by `name1` and `name2`. The -indices specify the appropriate block diagonal components of the direct sum -unitary vector `Ũ⃗`. - -TODO: Hessian not implemented -""" -function PairwiseQuadraticRegularizer( - Q::AbstractVector{<:Real}, - times::AbstractVector{Int}, - name1::Symbol, - name2::Symbol; - timestep_symbol::Symbol=:Δt, - eval_hessian::Bool=false, -) - params = Dict( - :type => :PairwiseQuadraticRegularizer, - :times => times, - :name => (name1, name2), - :Q => Q, - :eval_hessian => eval_hessian, - ) - - @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - J = 0.0 - for t ∈ times - if Z.timestep isa Symbol - Δt = Z⃗[slice(t, Z.components[timestep_symbol], Z.dim)] - else - Δt = Z.timestep - end - z1_t = Z⃗[slice(t, Z.components[name1], Z.dim)] - z2_t = Z⃗[slice(t, Z.components[name1], Z.dim)] - r_t = Δt * (z1_t .- z2_t) - J += 0.5 * r_t' * (Q .* r_t) - end - return J - end - - @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - ∇ = zeros(Z.dim * Z.T) - Threads.@threads for t ∈ times - z1_t_slice = slice(t, Z.components[name1], Z.dim) - z2_t_slice = slice(t, Z.components[name2], Z.dim) - z1_t = Z⃗[z1_t_slice] - z2_t = Z⃗[z2_t_slice] - - if Z.timestep isa Symbol - Δt_slice = slice(t, Z.components[timestep_symbol], Z.dim) - Δt = Z⃗[Δt_slice] - ∇[Δt_slice] .= (z1_t .- z2_t)' * (Q .* (Δt .* (z1_t .- z2_t))) - else - Δt = Z.timestep - end - - ∇[z1_t_slice] .= Q .* (Δt^2 * (z1_t .- z2_t)) - ∇[z2_t_slice] .= Q .* (Δt^2 * (z2_t .- z1_t)) - end - return ∇ - end - - # TODO: Hessian not implemented - ∂²L = nothing - ∂²L_structure = nothing - - if eval_hessian - throw(ErrorException("Hessian not implemented")) - end - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - -@doc raw""" -PairwiseQuadraticRegularizer( - Qs::AbstractVector{<:Real}, - graph::AbstractVector{<:AbstractVector{Symbol}}, - num_systems::Int; - name::Symbol=:Ũ⃗, - kwargs... - ) - -A convenience constructor for creating a PairwiseQuadraticRegularizer -for the trajectory component `name` with regularization strength `Qs` over the -graph `graph`. - -The regularizer is defined as - -```math -J_{Ũ⃗}(u) = \sum_{(i,j) \in E} \frac{1}{2} d(Ũ⃗_{i}, Ũ⃗_{j}; Q_{ij}) -``` - -where $d(Ũ⃗_{i}, Ũ⃗_{j}; Q_{ij})$ is the pairwise distance between the unitaries with -weight $Q_{ij}$. -""" -function PairwiseQuadraticRegularizer( - traj::NamedTrajectory, - Qs::Union{Float64, AbstractVector{<:Float64}}, - graph::AbstractVector{<:Tuple{Symbol, Symbol}}; - kwargs... -) - if isa(Qs, Float64) - Qs = Qs * ones(length(graph)) - end - @assert all(length(graph) == length(Qs)) "Graph and Qs must have same length" - - J = NullObjective() - for (Qᵢⱼ, (symb1, symb2)) ∈ zip(Qs, graph) - # Symbols should be the same size - dim = size(traj[symb1], 1) - J += PairwiseQuadraticRegularizer( - Qᵢⱼ * ones(dim), - 1:traj.T, - symb1, - symb2, - kwargs... - ) - end - - return J -end - -function PairwiseQuadraticRegularizer( - traj::NamedTrajectory, - Q::Float64, - name1::Symbol, - name2::Symbol; - kwargs... -) - return PairwiseQuadraticRegularizer( - traj, Q, [(name1, name2)]; - kwargs... - ) -end - -function QuadraticSmoothnessRegularizer(; - name::Symbol=nothing, - times::AbstractVector{Int}=1:traj.T, - R::AbstractVector{<:Real}=ones(traj.dims[name]), - eval_hessian=true -) - @assert !isnothing(name) "name must be specified" - @assert !isnothing(times) "times must be specified" - - params = Dict( - :type => :QuadraticSmoothnessRegularizer, - :name => name, - :times => times, - :R => R, - :eval_hessian => eval_hessian - ) - - @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - ∑Δv² = 0.0 - for t ∈ times[1:end-1] - vₜ₊₁ = Z⃗[slice(t + 1, Z.components[name], Z.dim)] - vₜ = Z⃗[slice(t, Z.components[name], Z.dim)] - Δv = vₜ₊₁ - vₜ - ∑Δv² += 0.5 * Δv' * (R .* Δv) - end - return ∑Δv² - end - - @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - ∇ = zeros(Z.dim * Z.T) - Threads.@threads for t ∈ times[1:end-1] - - vₜ_slice = slice(t, Z.components[name], Z.dim) - vₜ₊₁_slice = slice(t + 1, Z.components[name], Z.dim) - - vₜ = Z⃗[vₜ_slice] - vₜ₊₁ = Z⃗[vₜ₊₁_slice] - - Δv = vₜ₊₁ - vₜ - - ∇[vₜ_slice] += -R .* Δv - ∇[vₜ₊₁_slice] += R .* Δv - end - return ∇ - end - ∂²L = nothing - ∂²L_structure = nothing - - if eval_hessian - - ∂²L_structure = Z -> begin - structure = [] - # u smoothness regularizer Hessian main diagonal structure - - for t ∈ times - - vₜ_slice = slice(t, Z.components[name], Z.dim) - - # main diagonal (2 if t != 1 or T-1) * Rₛ I - # components: ∂²vₜSₜ - - append!( - structure, - collect( - zip( - vₜ_slice, - vₜ_slice - ) - ) - ) - end - - - # u smoothness regularizer Hessian off diagonal structure - - for t ∈ times[1:end-1] - - vₜ_slice = slice(t, Z.components[name], Z.dim) - vₜ₊₁_slice = slice(t + 1, Z.components[name], Z.dim) - - # off diagonal -Rₛ I components: ∂vₜ₊₁∂vₜSₜ - - append!( - structure, - collect( - zip( - vₜ_slice, - vₜ₊₁_slice - ) - ) - ) - end - return structure - end - - ∂²L = (Z⃗, Z) -> begin - - H = [] - - # u smoothness regularizer Hessian main diagonal values - - append!(H, R) - - for t in times[2:end-1] - append!(H, 2 * R) - end - - append!(H, R) - - - # u smoothness regularizer Hessian off diagonal values - - for t in times[1:end-1] - append!(H, -R) - end - - return H - end - end - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - -function QuadraticSmoothnessRegularizer( - name::Symbol, - traj::NamedTrajectory, - R::AbstractVector{<:Real}; - kwargs... -) - return QuadraticSmoothnessRegularizer(; - name=name, - times=1:traj.T, - R=R, - kwargs... - ) -end - -function QuadraticSmoothnessRegularizer( - name::Symbol, - traj::NamedTrajectory, - R::Real; - kwargs... -) - return QuadraticSmoothnessRegularizer(; - name=name, - times=1:traj.T, - R=R * ones(traj.dims[name]), - kwargs... - ) -end - -function L1Regularizer(; - name=nothing, - R::Vector{Float64}=nothing, - times=nothing, - eval_hessian=true -) - @assert !isnothing(name) "name must be specified" - @assert !isnothing(R) "R must be specified" - @assert !isnothing(times) "times must be specified" - - s1_name = Symbol("s1_$name") - s2_name = Symbol("s2_$name") - - params = Dict( - :type => :L1Regularizer, - :name => name, - :R => R, - :eval_hessian => eval_hessian, - :times => times, - ) - - L = (Z⃗, Z) -> sum( - dot( - R, - Z⃗[slice(t, Z.components[s1_name], Z.dim)] + - Z⃗[slice(t, Z.components[s2_name], Z.dim)] - ) for t ∈ times - ) - - ∇L = (Z⃗, Z) -> begin - ∇ = zeros(typeof(Z⃗[1]), length(Z⃗)) - Threads.@threads for t ∈ times - ∇[slice(t, Z.components[s1_name], Z.dim)] += R - ∇[slice(t, Z.components[s2_name], Z.dim)] += R - end - return ∇ - end - - if eval_hessian - ∂²L = (_, _) -> [] - ∂²L_structure = _ -> [] - else - ∂²L = nothing - ∂²L_structure = nothing - end - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - -@doc raw""" - L1Regularizer( - name::Symbol; - R_value::Float64=10.0, - R::Vector{Float64}=fill(R_value, length(indices)), - eval_hessian=true - ) - -Create an L1 regularizer for the trajectory component `name` with regularization -strength `R`. The regularizer is defined as - -```math -J_{L1}(u) = \sum_t \abs{R \cdot u_t} -``` -""" -function L1Regularizer( - name::Symbol, - traj::NamedTrajectory; - indices::AbstractVector{Int}=1:traj.dims[name], - times=(name ∈ keys(traj.initial) ? 2 : 1):traj.T, - R_value::Float64=10.0, - R::Vector{Float64}=fill(R_value, length(indices)), - eval_hessian=true -) - J = L1Regularizer(; - name=name, - R=R, - times=times, - eval_hessian=eval_hessian - ) - - slack_con = L1SlackConstraint(name, traj; indices=indices, times=times) - - return J, slack_con -end - -function L1Regularizer!( - constraints::Vector{<:AbstractConstraint}, - name::Symbol, - traj::NamedTrajectory; - kwargs... -) - J, slack_con = L1Regularizer(name, traj; kwargs...) - push!(constraints, slack_con) - return J -end - -function MinimumTimeObjective(; - D::Float64=1.0, - Δt_indices::AbstractVector{Int}=nothing, - eval_hessian::Bool=true -) - @assert !isnothing(Δt_indices) "Δt_indices must be specified" - - params = Dict( - :type => :MinimumTimeObjective, - :D => D, - :Δt_indices => Δt_indices, - :eval_hessian => eval_hessian - ) - - # TODO: amend this for case of no TimeStepsAllEqualConstraint - L(Z⃗::AbstractVector, Z::NamedTrajectory) = D * sum(Z⃗[Δt_indices]) - - ∇L = (Z⃗::AbstractVector, Z::NamedTrajectory) -> begin - ∇ = zeros(typeof(Z⃗[1]), length(Z⃗)) - ∇[Δt_indices] .= D - return ∇ - end - - if eval_hessian - ∂²L = (Z⃗, Z) -> [] - ∂²L_structure = Z -> [] - else - ∂²L = nothing - ∂²L_structure = nothing - end - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - -function MinimumTimeObjective(traj::NamedTrajectory; D=1.0, kwargs...) - @assert traj.timestep isa Symbol "trajectory does not have a dynamical timestep" - Δt_indices = [index(t, traj.components[traj.timestep][1], traj.dim) for t = 1:traj.T] - return MinimumTimeObjective(; D=D, Δt_indices=Δt_indices, kwargs...) -end - -@doc raw""" -InfidelityRobustnessObjective(; - H::::Union{EmbeddedOperator, AbstractMatrix{<:Number}, Nothing}=nothing, - eval_hessian::Bool=false, - symb::Symbol=:Ũ⃗ -) - -Create a control objective which penalizes the sensitivity of the infidelity to the provided -operator defined in the subspace of the control dynamics, thereby realizing robust control. - -The control dynamics are -```math -U_C(a)= \prod_t \exp{-i H_C(a_t)} -``` - -In the control frame, the H operator is (proportional to) -```math -R_{Robust}(a) = \frac{1}{T \norm{H_e}_2} \sum_t U_C(a_t)^\dag H_e U_C(a_t) \Delta t -``` -where we have adjusted to a unitless expression of the operator. - -The robustness objective is -```math -R_{Robust}(a) = \frac{1}{N} \norm{R}^2_F -``` -where N is the dimension of the Hilbert space. -""" -function InfidelityRobustnessObjective(; - H_error::Union{EmbeddedOperator, AbstractMatrix{<:Number}, Nothing}=nothing, - eval_hessian::Bool=false, - symb::Symbol=:Ũ⃗ -) - @assert !isnothing(H_error) "H_error must be specified" - - # Indices of all non-zero subspace components for iso_vec_operators - if H_error isa EmbeddedOperator - H = unembed(H_error) - subspace = get_unitary_isomorphism_subspace_indices(H_error) - else - H = H_error - subspace = 1:length(operator_to_iso_vec(H)) - end - - @views function get_timesteps(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - return map(1:Z.T) do t - if Z.timestep isa Symbol - Z⃗[slice(t, Z.components[Z.timestep], Z.dim)][1] - else - Z.timestep - end - end - end - - # Control frame - @views function rotate(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - Δts = get_timesteps(Z⃗, Z) - T = sum(Δts) - Z_comps = Z.components[symb][subspace] - R = sum( - map(1:Z.T) do t - Uₜ = iso_vec_to_operator(Z⃗[slice(t, Z_comps, Z.dim)]) - Uₜ'H*Uₜ .* Δts[t] - end - ) / norm(H) / T - return R - end - - function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - R = rotate(Z⃗, Z) - return real(tr(R'R)) / size(R, 1) - end - - @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - ∇ = zeros(Z.dim * Z.T) - R = rotate(Z⃗, Z) - Δts = get_timesteps(Z⃗, Z) - Z_comps = Z.components[symb][subspace] - T = sum(Δts) - units = 1 / norm(H) / T - Threads.@threads for t ∈ 1:Z.T - # State - Uₜ_slice = slice(t, Z_comps, Z.dim) - Uₜ = iso_vec_to_operator(Z⃗[Uₜ_slice]) - - # State gradient - ∇[Uₜ_slice] .= operator_to_iso_vec(2 * H * Uₜ * R * Δts[t]) * units - - # Time gradient - if Z.timestep isa Symbol - ∂R = Uₜ'H*Uₜ - ∇[slice(t, Z.components[Z.timestep], Z.dim)] .= tr(∂R*R + R*∂R) * units - end - end - return ∇ / size(R, 1) - end - - # Hessian is dense (Control frame R contains sum over all unitaries). - if eval_hessian - # TODO - ∂²L = (Z⃗, Z) -> [] - ∂²L_structure = Z -> [] - else - ∂²L = nothing - ∂²L_structure = nothing - end - - params = Dict( - :type => :InfidelityRobustnessObjective, - :H_error => H_error, - :eval_hessian => eval_hessian, - :symb => symb - ) - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - -""" - PairwiseInfidelityRobustnessObjective(; - H1::Union{EmbeddedOperator, AbstractMatrix{<:Number}, Nothing}=nothing, - H2_error::Union{EmbeddedOperator, AbstractMatrix{<:Number}, Nothing}=nothing, - symb1::Symbol=:Ũ⃗1, - symb2::Symbol=:Ũ⃗2, - eval_hessian::Bool=false, - ) - -Create a control objective which penalizes the sensitivity of the infidelity to the provided operators -defined in the subspaces of the control dynamics, thereby realizing robust control. -""" -function PairwiseInfidelityRobustnessObjective(; - H1_error::Union{EmbeddedOperator, AbstractMatrix{<:Number}, Nothing}=nothing, - H2_error::Union{EmbeddedOperator, AbstractMatrix{<:Number}, Nothing}=nothing, - symb1::Symbol=:Ũ⃗1, - symb2::Symbol=:Ũ⃗2, - eval_hessian::Bool=false, -) - @assert !isnothing(H1_error) "H1_error must be specified" - @assert !isnothing(H2_error) "H2_error must be specified" - - if H1_error isa EmbeddedOperator - H1 = unembed(H1_error) - subspace1 = get_unitary_isomorphism_subspace_indices(H1_error) - else - H1 = H1_error - subspace1 = 1:length(operator_to_iso_vec(H1)) - end - - if H2_error isa EmbeddedOperator - H2 = unembed(H2_error) - subspace2 = get_unitary_isomorphism_subspace_indices(H2_error) - else - H2 = H2_error - subspace2 = 1:length(operator_to_iso_vec(H2)) - end - - @views function get_timesteps(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - return map(1:Z.T) do t - if Z.timestep isa Symbol - Z⃗[slice(t, Z.components[Z.timestep], Z.dim)][1] - else - Z.timestep - end - end - end - - function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - Δts = get_timesteps(Z⃗, Z) - Z1_comps = Z.components[symb1][subspace1] - Z2_comps = Z.components[symb2][subspace2] - T = sum(Δts) - R = 0.0 - for (i₁, Δt₁) ∈ enumerate(Δts) - for (i₂, Δt₂) ∈ enumerate(Δts) - # States - U1ₜ₁ = iso_vec_to_operator(Z⃗[slice(i₁, Z1_comps, Z.dim)]) - U1ₜ₂ = iso_vec_to_operator(Z⃗[slice(i₂, Z1_comps, Z.dim)]) - U2ₜ₁ = iso_vec_to_operator(Z⃗[slice(i₁, Z2_comps, Z.dim)]) - U2ₜ₂ = iso_vec_to_operator(Z⃗[slice(i₂, Z2_comps, Z.dim)]) - - # Rotating frame - rH1ₜ₁ = U1ₜ₁'H1*U1ₜ₁ - rH1ₜ₂ = U1ₜ₂'H1*U1ₜ₂ - rH2ₜ₁ = U2ₜ₁'H2*U2ₜ₁ - rH2ₜ₂ = U2ₜ₂'H2*U2ₜ₂ - - # Robustness - units = 1 / T^2 / norm(H1)^2 / norm(H2)^2 - R += real(tr(rH1ₜ₁'rH1ₜ₂) * tr(rH2ₜ₁'rH2ₜ₂) * Δt₁ * Δt₂ * units) - end - end - return R / size(H1, 1) / size(H2, 1) - end - - @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) - ∇ = zeros(Z.dim * Z.T) - Δts = get_timesteps(Z⃗, Z) - Z1_comps = Z.components[symb1][subspace1] - Z2_comps = Z.components[symb2][subspace2] - T = sum(Δts) - Threads.@threads for (i₁, i₂) ∈ vec(collect(Iterators.product(1:Z.T, 1:Z.T))) - # Times - Δt₁ = Δts[i₁] - Δt₂ = Δts[i₂] - - # States - U1ₜ₁_slice = slice(i₁, Z1_comps, Z.dim) - U1ₜ₂_slice = slice(i₂, Z1_comps, Z.dim) - U2ₜ₁_slice = slice(i₁, Z2_comps, Z.dim) - U2ₜ₂_slice = slice(i₂, Z2_comps, Z.dim) - U1ₜ₁ = iso_vec_to_operator(Z⃗[U1ₜ₁_slice]) - U1ₜ₂ = iso_vec_to_operator(Z⃗[U1ₜ₂_slice]) - U2ₜ₁ = iso_vec_to_operator(Z⃗[U2ₜ₁_slice]) - U2ₜ₂ = iso_vec_to_operator(Z⃗[U2ₜ₂_slice]) - - # Rotating frame - rH1ₜ₁ = U1ₜ₁'H1*U1ₜ₁ - rH1ₜ₂ = U1ₜ₂'H1*U1ₜ₂ - rH2ₜ₁ = U2ₜ₁'H2*U2ₜ₁ - rH2ₜ₂ = U2ₜ₂'H2*U2ₜ₂ - - # ∇Uiₜⱼ (assume H's are Hermitian) - units = 1 / T^2 / norm(H1)^2 / norm(H2)^2 - R1 = tr(rH1ₜ₁'rH1ₜ₂) * Δt₁ * Δt₂ * units - R2 = tr(rH2ₜ₁'rH2ₜ₂) * Δt₁ * Δt₂ * units - ∇[U1ₜ₁_slice] += operator_to_iso_vec(2 * H1 * U1ₜ₁ * rH1ₜ₂) * R2 - ∇[U1ₜ₂_slice] += operator_to_iso_vec(2 * H1 * U1ₜ₂ * rH1ₜ₁) * R2 - ∇[U2ₜ₁_slice] += operator_to_iso_vec(2 * H2 * U2ₜ₁ * rH2ₜ₂) * R1 - ∇[U2ₜ₂_slice] += operator_to_iso_vec(2 * H2 * U2ₜ₂ * rH2ₜ₁) * R1 - - # Time gradients - if Z.timestep isa Symbol - R = real(tr(rH1ₜ₁'rH1ₜ₂) * tr(rH2ₜ₁'rH2ₜ₂)) * units - ∇[slice(i₁, Z.components[Z.timestep], Z.dim)] .= R * Δt₂ - ∇[slice(i₂, Z.components[Z.timestep], Z.dim)] .= R * Δt₁ - end - end - return ∇ / size(H1, 1) / size(H2, 1) - end - - # Hessian is dense (Control frame R contains sum over all unitaries). - if eval_hessian - # TODO - ∂²L = (Z⃗, Z) -> [] - ∂²L_structure = Z -> [] - else - ∂²L = nothing - ∂²L_structure = nothing - end - - params = Dict( - :type => :PairwiseInfidelityRobustnessObjective, - :H1_error => H1_error, - :H2_error => H2_error, - :symb1 => symb1, - :symb2 => symb2, - :eval_hessian => eval_hessian - ) - - return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) -end - - -end diff --git a/src/objectives/_objectives.jl b/src/objectives/_objectives.jl new file mode 100644 index 00000000..8985fbae --- /dev/null +++ b/src/objectives/_objectives.jl @@ -0,0 +1,128 @@ +module Objectives + +export Objective +export NullObjective + +using ..Isomorphisms +using ..QuantumSystems +using ..EmbeddedOperators +using ..Losses +using ..Constraints + +using TrajectoryIndexingUtils +using NamedTrajectories + +using LinearAlgebra +using SparseArrays +using Symbolics +using TestItemRunner + +include("quantum_objective.jl") +include("unitary_infidelity_objective.jl") +include("regularizer_objective.jl") +include("minimum_time_objective.jl") +include("unitary_robustness_objective.jl") + +# TODO: +# - [ ] Do not reference the Z object in the objective (components only / remove "name") + +""" + sparse_to_moi(A::SparseMatrixCSC) + +Converts a sparse matrix to tuple of vector of nonzero indices and vector of nonzero values +""" +function sparse_to_moi(A::SparseMatrixCSC) + inds = collect(zip(findnz(A)...)) + vals = [A[i,j] for (i,j) ∈ inds] + return (inds, vals) +end + +# ----------------------------------------------------------------------------- # +# Objective # +# ----------------------------------------------------------------------------- # + +""" + Objective + +A structure for defining objective functions. + +The `terms` field contains all the arguments needed to construct the objective function. + +Fields: + `L`: the objective function + `∇L`: the gradient of the objective function + `∂²L`: the Hessian of the objective function + `∂²L_structure`: the structure of the Hessian of the objective function + `terms`: a vector of dictionaries containing the terms of the objective function +""" +struct Objective + L::Function + ∇L::Function + ∂²L::Union{Function, Nothing} + ∂²L_structure::Union{Function, Nothing} + terms::Vector{Dict} +end + +function Base.:+(obj1::Objective, obj2::Objective) + L = (Z⃗, Z) -> obj1.L(Z⃗, Z) + obj2.L(Z⃗, Z) + ∇L = (Z⃗, Z) -> obj1.∇L(Z⃗, Z) + obj2.∇L(Z⃗, Z) + if isnothing(obj1.∂²L) && isnothing(obj2.∂²L) + ∂²L = Nothing + ∂²L_structure = Nothing + elseif isnothing(obj1.∂²L) + ∂²L = (Z⃗, Z) -> obj2.∂²L(Z⃗, Z) + ∂²L_structure = obj2.∂²L_structure + elseif isnothing(obj2.∂²L) + ∂²L = (Z⃗, Z) -> obj1.∂²L(Z⃗, Z) + ∂²L_structure = obj1.∂²L_structure + else + ∂²L = (Z⃗, Z) -> vcat(obj1.∂²L(Z⃗, Z), obj2.∂²L(Z⃗, Z)) + ∂²L_structure = Z -> vcat(obj1.∂²L_structure(Z), obj2.∂²L_structure(Z)) + end + terms = vcat(obj1.terms, obj2.terms) + return Objective(L, ∇L, ∂²L, ∂²L_structure, terms) +end + +Base.:+(obj::Objective, ::Nothing) = obj +Base.:+(obj::Objective) = obj + +function Objective(terms::AbstractVector{<:Dict}) + return +(Objective.(terms)...) +end + +function Base.:*(num::Real, obj::Objective) + L = (Z⃗, Z) -> num * obj.L(Z⃗, Z) + ∇L = (Z⃗, Z) -> num * obj.∇L(Z⃗, Z) + if isnothing(obj.∂²L) + ∂²L = nothing + ∂²L_structure = nothing + else + ∂²L = (Z⃗, Z) -> num * obj.∂²L(Z⃗, Z) + ∂²L_structure = obj.∂²L_structure + end + return Objective(L, ∇L, ∂²L, ∂²L_structure, obj.terms) +end + +Base.:*(obj::Objective, num::Real) = num * obj + +function Objective(term::Dict) + return eval(term[:type])(; delete!(term, :type)...) +end + +# ----------------------------------------------------------------------------- # +# Null objective # +# ----------------------------------------------------------------------------- # + +function NullObjective() + params = Dict(:type => :NullObjective) + L(Z⃗::AbstractVector{R}, Z::NamedTrajectory) where R<:Real = 0.0 + ∇L(Z⃗::AbstractVector{R}, Z::NamedTrajectory) where R<:Real = zeros(R, Z.dim * Z.T + Z.global_dim) + ∂²L_structure(Z::NamedTrajectory) = [] + function ∂²L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory; return_moi_vals=true) + n = Z.dim * Z.T + Z.global_dim + return return_moi_vals ? [] : spzeros(n, n) + end + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +end diff --git a/src/objectives/minimum_time_objective.jl b/src/objectives/minimum_time_objective.jl new file mode 100644 index 00000000..618e82fb --- /dev/null +++ b/src/objectives/minimum_time_objective.jl @@ -0,0 +1,57 @@ +export MinimumTimeObjective + + +""" + MinimumTimeObjective + +A type of objective that counts the time taken to complete a task. + +Fields: + `D`: a scaling factor + `Δt_indices`: the indices of the time steps + `eval_hessian`: whether to evaluate the Hessian + +""" +function MinimumTimeObjective(; + D::Float64=1.0, + Δt_indices::AbstractVector{Int}=nothing, + eval_hessian::Bool=true +) + @assert !isnothing(Δt_indices) "Δt_indices must be specified" + + params = Dict( + :type => :MinimumTimeObjective, + :D => D, + :Δt_indices => Δt_indices, + :eval_hessian => eval_hessian + ) + + # TODO: amend this for case of no TimeStepsAllEqualConstraint + L(Z⃗::AbstractVector, Z::NamedTrajectory) = D * sum(Z⃗[Δt_indices]) + + ∇L = (Z⃗::AbstractVector, Z::NamedTrajectory) -> begin + ∇ = zeros(typeof(Z⃗[1]), length(Z⃗)) + ∇[Δt_indices] .= D + return ∇ + end + + if eval_hessian + ∂²L = (Z⃗, Z) -> [] + ∂²L_structure = Z -> [] + else + ∂²L = nothing + ∂²L_structure = nothing + end + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +function MinimumTimeObjective( + traj::NamedTrajectory; + D=1.0, + kwargs... +) + @assert traj.timestep isa Symbol "trajectory does not have a dynamical timestep" + Δt_indices = [index(t, traj.components[traj.timestep][1], traj.dim) for t = 1:traj.T] + return MinimumTimeObjective(; D=D, Δt_indices=Δt_indices, kwargs...) +end \ No newline at end of file diff --git a/src/objectives/quantum_objective.jl b/src/objectives/quantum_objective.jl new file mode 100644 index 00000000..2d129005 --- /dev/null +++ b/src/objectives/quantum_objective.jl @@ -0,0 +1,217 @@ +export QuantumObjective +export QuantumUnitaryObjective +export QuantumStateObjective + +### +### QuantumObjective +### + +""" + QuantumObjective + + A generic objective function for quantum trajectories that use a loss. + +""" +function QuantumObjective(; + names::Union{Nothing,Tuple{Vararg{Symbol}}}=nothing, + name::Union{Nothing,Symbol}=nothing, + goals::Union{Nothing,AbstractVector{<:Real},Tuple{Vararg{AbstractVector{<:Real}}}}=nothing, + loss::Symbol=:InfidelityLoss, + Q::Union{Float64, Vector{Float64}}=100.0, + eval_hessian::Bool=true +) + @assert !(isnothing(names) && isnothing(name)) "name or names must be specified" + @assert !isnothing(goals) "goals corresponding to names must be specified" + + if isnothing(names) + names = (name,) + end + + if goals isa AbstractVector + goals = (goals,) + end + + if Q isa Float64 + Q = ones(length(names)) * Q + else + @assert length(Q) == length(names) + end + + params = Dict( + :type => :QuantumObjective, + :names => names, + :goals => goals, + :loss => loss, + :Q => Q, + :eval_hessian => eval_hessian, + ) + + losses = [eval(loss)(name, goal) for (name, goal) ∈ zip(names, goals)] + + @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + loss = 0.0 + for (Qᵢ, lᵢ, name) ∈ zip(Q, losses, names) + name_slice = slice(Z.T, Z.components[name], Z.dim) + loss += Qᵢ * lᵢ(Z⃗[name_slice]) + end + return loss + end + + @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + ∇ = zeros(Z.dim * Z.T + Z.global_dim) + for (Qᵢ, lᵢ, name) ∈ zip(Q, losses, names) + name_slice = slice(Z.T, Z.components[name], Z.dim) + ∇[name_slice] = Qᵢ * lᵢ(Z⃗[name_slice]; gradient=true) + end + return ∇ + end + + function ∂²L_structure(Z::NamedTrajectory) + structure = [] + final_time_offset = index(Z.T, 0, Z.dim) + for (name, loss) ∈ zip(names, losses) + comp_start_offset = Z.components[name][1] - 1 + comp_hessian_structure = [ + ij .+ (final_time_offset + comp_start_offset) + for ij ∈ loss.∇²l_structure + ] + append!(structure, comp_hessian_structure) + end + return structure + end + + + @views function ∂²L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory; return_moi_vals=true) + H = spzeros(Z.dim * Z.T + Z.global_dim, Z.dim * Z.T + Z.global_dim) + for (Qᵢ, name, lᵢ) ∈ zip(Q, names, losses) + name_slice = slice(Z.T, Z.components[name], Z.dim) + H[name_slice, name_slice] = + Qᵢ * lᵢ(Z⃗[name_slice]; hessian=true) + end + if return_moi_vals + Hs = [H[i,j] for (i, j) ∈ ∂²L_structure(Z)] + return Hs + else + return H + end + end + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +function QuantumObjective( + name::Symbol, + traj::NamedTrajectory, + loss::Symbol, + Q::Float64 +) + goal = traj.goal[name] + return QuantumObjective(name=name, goals=goal, loss=loss, Q=Q) +end + +function QuantumObjective( + names::Tuple{Vararg{Symbol}}, + traj::NamedTrajectory, + loss::Symbol, + Q::Float64 +) + goals = Tuple(traj.goal[name] for name in names) + return QuantumObjective(names=names, goals=goals, loss=loss, Q=Q) +end + +### +### Example: Default quantum objectives +### + +function QuantumUnitaryObjective( + name::Symbol, + traj::NamedTrajectory, + Q::Float64 +) + return QuantumObjective(name, traj, :UnitaryInfidelityLoss, Q) +end + +function QuantumStateObjective( + name::Symbol, + traj::NamedTrajectory, + Q::Float64 +) + return QuantumObjective(name, traj, :InfidelityLoss, Q) +end + +# ============================================================================= # + +@testitem "Quantum State Objective" begin + using LinearAlgebra + using NamedTrajectories + using ForwardDiff + include("../../test/test_utils.jl") + + T = 10 + + Z = NamedTrajectory( + (ψ̃ = randn(4, T), u = randn(2, T)), + controls=:u, + timestep=0.1, + goal=(ψ̃ = [1., 0., 0., 0.],) + ) + + loss = :InfidelityLoss + Q = 100.0 + + J = QuantumObjective(:ψ̃, Z, loss, Q) + + L = Z⃗ -> J.L(Z⃗, Z) + ∇L = Z⃗ -> J.∇L(Z⃗, Z) + ∂²L = Z⃗ -> J.∂²L(Z⃗, Z) + ∂²L_structure = J.∂²L_structure(Z) + + # test objective function gradient + @test ForwardDiff.gradient(L, Z.datavec) ≈ ∇L(Z.datavec) + + # test objective function hessian + shape = (Z.dim * Z.T + Z.global_dim, Z.dim * Z.T + Z.global_dim) + @test ForwardDiff.hessian(L, Z.datavec) ≈ dense(∂²L(Z.datavec), ∂²L_structure, shape) +end + +@testitem "Unitary Objective" begin + using LinearAlgebra + using NamedTrajectories + using ForwardDiff + include("../../test/test_utils.jl") + + T = 10 + + U_init = GATES[:I] + U_goal = GATES[:X] + + Ũ⃗_init = operator_to_iso_vec(U_init) + Ũ⃗_goal = operator_to_iso_vec(U_goal) + + Z = NamedTrajectory( + (Ũ⃗ = randn(length(Ũ⃗_init), T), u = randn(2, T)), + controls=:u, + timestep=0.1, + initial=(Ũ⃗ = Ũ⃗_init,), + goal=(Ũ⃗ = Ũ⃗_goal,) + ) + + loss = :UnitaryInfidelityLoss + Q = 100.0 + + J = QuantumObjective(:Ũ⃗, Z, loss, Q) + + L = Z⃗ -> J.L(Z⃗, Z) + ∇L = Z⃗ -> J.∇L(Z⃗, Z) + ∂²L = Z⃗ -> J.∂²L(Z⃗, Z) + ∂²L_structure = J.∂²L_structure(Z) + + # test objective function gradient + @test all(ForwardDiff.gradient(L, Z.datavec) ≈ ∇L(Z.datavec)) + + # test objective function hessian + shape = (Z.dim * Z.T + Z.global_dim, Z.dim * Z.T + Z.global_dim) + H = dense(∂²L(Z.datavec), ∂²L_structure, shape) + H_forwarddiff = ForwardDiff.hessian(L, Z.datavec) + @test all(H .≈ H_forwarddiff) +end \ No newline at end of file diff --git a/src/objectives/regularizer_objective.jl b/src/objectives/regularizer_objective.jl new file mode 100644 index 00000000..c9339fa4 --- /dev/null +++ b/src/objectives/regularizer_objective.jl @@ -0,0 +1,633 @@ +export QuadraticRegularizer +export QuadraticSmoothnessRegularizer +export L1Regularizer +export L1Regularizer! +export PairwiseQuadraticRegularizer + + +### +### Quadratic Regularizer +### + +""" + QuadraticRegularizer + +A quadratic regularizer for a trajectory component. + +Fields: + `name`: the name of the trajectory component to regularize + `times`: the times at which to evaluate the regularizer + `dim`: the dimension of the trajectory component + `R`: the regularization matrix + `baseline`: the baseline values for the trajectory component + `eval_hessian`: whether to evaluate the Hessian of the regularizer + `timestep_symbol`: the symbol for the timestep variable +""" +function QuadraticRegularizer(; + name::Union{Nothing, Symbol}=nothing, + times::Union{Nothing, AbstractVector{Int}}=nothing, + dim::Union{Nothing, Int}=nothing, + R::Union{Nothing, AbstractVector{<:Real}}=nothing, + baseline::Union{Nothing, AbstractArray{<:Real}}=nothing, + eval_hessian::Bool=true, + timestep_symbol::Symbol=:Δt +) + + @assert !isnothing(name) "name must be specified" + @assert !isnothing(times) "times must be specified" + @assert !isnothing(dim) "dim must be specified" + @assert !isnothing(R) "R must be specified" + if isnothing(baseline) + baseline = zeros(length(R), length(times)) + else + if size(baseline) != (length(R), length(times)) + throw(ArgumentError("size(baseline)=$(size(baseline)) must match $(length(R)) x $(length(times))")) + end + end + + params = Dict( + :type => :QuadraticRegularizer, + :name => name, + :times => times, + :dim => dim, + :baseline => baseline, + :R => R, + :eval_hessian => eval_hessian + ) + + @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + J = 0.0 + for t ∈ times + if Z.timestep isa Symbol + Δt = Z⃗[slice(t, Z.components[timestep_symbol], Z.dim)] + else + Δt = Z.timestep + end + + vₜ = Z⃗[slice(t, Z.components[name], Z.dim)] + Δv = vₜ .- baseline[:, t] + + rₜ = Δt .* Δv + J += 0.5 * rₜ' * (R .* rₜ) + end + return J + end + + @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + ∇ = zeros(Z.dim * Z.T + Z.global_dim) + Threads.@threads for t ∈ times + vₜ_slice = slice(t, Z.components[name], Z.dim) + Δv = Z⃗[vₜ_slice] .- baseline[:, t] + + if Z.timestep isa Symbol + Δt_slice = slice(t, Z.components[timestep_symbol], Z.dim) + Δt = Z⃗[Δt_slice] + ∇[Δt_slice] .= Δv' * (R .* (Δt .* Δv)) + else + Δt = Z.timestep + end + + ∇[vₜ_slice] .= R .* (Δt.^2 .* Δv) + end + return ∇ + end + + ∂²L = nothing + ∂²L_structure = nothing + + if eval_hessian + + ∂²L_structure = Z -> begin + structure = [] + # Hessian structure (eq. 17) + for t ∈ times + vₜ_slice = slice(t, Z.components[name], Z.dim) + vₜ_vₜ_inds = collect(zip(vₜ_slice, vₜ_slice)) + append!(structure, vₜ_vₜ_inds) + + if Z.timestep isa Symbol + Δt_slice = slice(t, Z.components[timestep_symbol], Z.dim) + # ∂²_vₜ_Δt + vₜ_Δt_inds = [(i, j) for i ∈ vₜ_slice for j ∈ Δt_slice] + append!(structure, vₜ_Δt_inds) + # ∂²_Δt_vₜ + Δt_vₜ_inds = [(i, j) for i ∈ Δt_slice for j ∈ vₜ_slice] + append!(structure, Δt_vₜ_inds) + # ∂²_Δt_Δt + Δt_Δt_inds = collect(zip(Δt_slice, Δt_slice)) + append!(structure, Δt_Δt_inds) + end + end + return structure + end + + ∂²L = (Z⃗, Z) -> begin + values = [] + # Match Hessian structure indices + for t ∈ times + if Z.timestep isa Symbol + Δt = Z⃗[slice(t, Z.components[timestep_symbol], Z.dim)] + append!(values, R .* Δt.^2) + # ∂²_vₜ_Δt, ∂²_Δt_vₜ + vₜ = Z⃗[slice(t, Z.components[name], Z.dim)] + Δv = vₜ .- baseline[:, t] + append!(values, 2 * (R .* (Δt .* Δv))) + append!(values, 2 * (R .* (Δt .* Δv))) + # ∂²_Δt_Δt + append!(values, Δv' * (R .* Δv)) + else + Δt = Z.timestep + append!(values, R .* Δt.^2) + end + end + return values + end + end + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +function QuadraticRegularizer( + name::Symbol, + traj::NamedTrajectory, + R::AbstractVector{<:Real}; + kwargs... +) + return QuadraticRegularizer(; + name=name, + times=1:traj.T, + dim=traj.dim, + R=R, + kwargs... + ) +end + +function QuadraticRegularizer( + name::Symbol, + traj::NamedTrajectory, + R::Real; + kwargs... +) + return QuadraticRegularizer(; + name=name, + times=1:traj.T, + dim=traj.dim, + R=R * ones(traj.dims[name]), + kwargs... + ) +end + +### +### QuadraticSmoothnessRegularizer +### + +""" + QuadraticSmoothnessRegularizer + +A quadratic smoothness regularizer for a trajectory component. + +Fields: + `name`: the name of the trajectory component to regularize + `times`: the times at which to evaluate the regularizer + `R`: the regularization matrix + `eval_hessian`: whether to evaluate the Hessian of the regularizer +""" +function QuadraticSmoothnessRegularizer(; + name::Symbol=nothing, + times::Union{Nothing, AbstractVector{Int}}=nothing, + R::Union{Nothing, AbstractVector{<:Real}}=nothing, + eval_hessian=true +) + @assert !isnothing(name) "name must be specified" + @assert !isnothing(times) "times must be specified" + + params = Dict( + :type => :QuadraticSmoothnessRegularizer, + :name => name, + :times => times, + :R => R, + :eval_hessian => eval_hessian + ) + + @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + ∑Δv² = 0.0 + for t ∈ times[1:end-1] + vₜ₊₁ = Z⃗[slice(t + 1, Z.components[name], Z.dim)] + vₜ = Z⃗[slice(t, Z.components[name], Z.dim)] + Δv = vₜ₊₁ - vₜ + ∑Δv² += 0.5 * Δv' * (R .* Δv) + end + return ∑Δv² + end + + @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + ∇ = zeros(Z.dim * Z.T + Z.global_dim) + Threads.@threads for t ∈ times[1:end-1] + + vₜ_slice = slice(t, Z.components[name], Z.dim) + vₜ₊₁_slice = slice(t + 1, Z.components[name], Z.dim) + + vₜ = Z⃗[vₜ_slice] + vₜ₊₁ = Z⃗[vₜ₊₁_slice] + + Δv = vₜ₊₁ - vₜ + + ∇[vₜ_slice] += -R .* Δv + ∇[vₜ₊₁_slice] += R .* Δv + end + return ∇ + end + ∂²L = nothing + ∂²L_structure = nothing + + if eval_hessian + + ∂²L_structure = Z -> begin + structure = [] + + # u smoothness regularizer Hessian main diagonal structure + for t ∈ times + vₜ_slice = slice(t, Z.components[name], Z.dim) + + # main diagonal (2 if t != 1 or T-1) * Rₛ I + # components: ∂²vₜSₜ + append!( + structure, + collect(zip(vₜ_slice, vₜ_slice)) + ) + end + + # u smoothness regularizer Hessian off diagonal structure + for t ∈ times[1:end-1] + vₜ_slice = slice(t, Z.components[name], Z.dim) + vₜ₊₁_slice = slice(t + 1, Z.components[name], Z.dim) + + # off diagonal -Rₛ I components: ∂vₜ₊₁∂vₜSₜ + append!( + structure, + collect(zip(vₜ_slice, vₜ₊₁_slice)) + ) + end + return structure + end + + ∂²L = (Z⃗, Z) -> begin + + H = [] + + # u smoothness regularizer Hessian main diagonal values + append!(H, R) + for t in times[2:end-1] + append!(H, 2 * R) + end + append!(H, R) + + # u smoothness regularizer Hessian off diagonal values + for t in times[1:end-1] + append!(H, -R) + end + return H + end + end + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +function QuadraticSmoothnessRegularizer( + name::Symbol, + traj::NamedTrajectory, + R::AbstractVector{<:Real}; + kwargs... +) + return QuadraticSmoothnessRegularizer(; + name=name, + times=1:traj.T, + R=R, + kwargs... + ) +end + +function QuadraticSmoothnessRegularizer( + name::Symbol, + traj::NamedTrajectory, + R::Real; + kwargs... +) + return QuadraticSmoothnessRegularizer(; + name=name, + times=1:traj.T, + R=R * ones(traj.dims[name]), + kwargs... + ) +end + +### +### L1Regularizer +### + +@doc raw""" + L1Regularizer + +Create an L1 regularizer for the trajectory component. The regularizer is defined as + +```math +J_{L1}(u) = \sum_t \abs{R \cdot u_t} +``` + +where \(R\) is the regularization matrix and \(u_t\) is the trajectory component at time \(t\). + + +""" +function L1Regularizer(; + name=nothing, + R::Vector{Float64}=nothing, + times=nothing, + eval_hessian=true +) + @assert !isnothing(name) "name must be specified" + @assert !isnothing(R) "R must be specified" + @assert !isnothing(times) "times must be specified" + + s1_name = Symbol("s1_$name") + s2_name = Symbol("s2_$name") + + params = Dict( + :type => :L1Regularizer, + :name => name, + :R => R, + :eval_hessian => eval_hessian, + :times => times, + ) + + L = (Z⃗, Z) -> sum( + dot( + R, + Z⃗[slice(t, Z.components[s1_name], Z.dim)] + + Z⃗[slice(t, Z.components[s2_name], Z.dim)] + ) for t ∈ times + ) + + ∇L = (Z⃗, Z) -> begin + ∇ = zeros(typeof(Z⃗[1]), length(Z⃗)) + Threads.@threads for t ∈ times + ∇[slice(t, Z.components[s1_name], Z.dim)] += R + ∇[slice(t, Z.components[s2_name], Z.dim)] += R + end + return ∇ + end + + if eval_hessian + ∂²L = (_, _) -> [] + ∂²L_structure = _ -> [] + else + ∂²L = nothing + ∂²L_structure = nothing + end + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +function L1Regularizer( + name::Symbol, + traj::NamedTrajectory; + indices::AbstractVector{Int}=1:traj.dims[name], + times=(name ∈ keys(traj.initial) ? 2 : 1):traj.T, + R_value::Float64=10.0, + R::Vector{Float64}=fill(R_value, length(indices)), + eval_hessian=true +) + J = L1Regularizer(; + name=name, + R=R, + times=times, + eval_hessian=eval_hessian + ) + + slack_con = L1SlackConstraint(name, traj; indices=indices, times=times) + + return J, slack_con +end + +function L1Regularizer!( + constraints::Vector{<:AbstractConstraint}, + name::Symbol, + traj::NamedTrajectory; + kwargs... +) + J, slack_con = L1Regularizer(name, traj; kwargs...) + push!(constraints, slack_con) + return J +end + +### +### PairwiseQuadraticRegularizer +### + +@doc raw""" + PairwiseQuadraticRegularizer + +Create a pairwise quadratic regularizer for the trajectory component `name` with +regularization strength `R`. The regularizer is defined as + +```math + J_{v⃗}(u) = \sum_t \frac{1}{2} \Delta t_t^2 (v⃗_{1,t} - v⃗_{2,t})^T R (v⃗_{1,t} - v⃗_{2,t}) +``` + +where $v⃗_{1}$ and $v⃗_{2}$ are selected by `name1` and `name2`. The indices specify the +appropriate block diagonal components of the direct sum vector `v⃗`. + +TODO: Hessian not implemented + + +Fields: + `R`: the regularization strength + `times`: the time steps to apply the regularizer + `name1`: the first name + `name2`: the second name + `timestep_symbol`: the symbol for the timestep + `eval_hessian`: whether to evaluate the Hessian +""" +function PairwiseQuadraticRegularizer( + R::AbstractVector{<:Real}, + times::AbstractVector{Int}, + name1::Symbol, + name2::Symbol; + timestep_symbol::Symbol=:Δt, + eval_hessian::Bool=false, +) + params = Dict( + :type => :PairwiseQuadraticRegularizer, + :times => times, + :name => (name1, name2), + :R => R, + :eval_hessian => eval_hessian, + ) + + @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + J = 0.0 + for t ∈ times + if Z.timestep isa Symbol + Δt = Z⃗[slice(t, Z.components[timestep_symbol], Z.dim)] + else + Δt = Z.timestep + end + z1_t = Z⃗[slice(t, Z.components[name1], Z.dim)] + z2_t = Z⃗[slice(t, Z.components[name1], Z.dim)] + r_t = Δt * (z1_t .- z2_t) + J += 0.5 * r_t' * (R .* r_t) + end + return J + end + + @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + ∇ = zeros(Z.dim * Z.T + Z.global_dim) + Threads.@threads for t ∈ times + z1_t_slice = slice(t, Z.components[name1], Z.dim) + z2_t_slice = slice(t, Z.components[name2], Z.dim) + z1_t = Z⃗[z1_t_slice] + z2_t = Z⃗[z2_t_slice] + + if Z.timestep isa Symbol + Δt_slice = slice(t, Z.components[timestep_symbol], Z.dim) + Δt = Z⃗[Δt_slice] + ∇[Δt_slice] .= (z1_t .- z2_t)' * (R .* (Δt .* (z1_t .- z2_t))) + else + Δt = Z.timestep + end + + ∇[z1_t_slice] .= R .* (Δt^2 * (z1_t .- z2_t)) + ∇[z2_t_slice] .= R .* (Δt^2 * (z2_t .- z1_t)) + end + return ∇ + end + + # TODO: Hessian not implemented + ∂²L = nothing + ∂²L_structure = nothing + + if eval_hessian + throw(ErrorException("Hessian not implemented")) + end + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +@doc raw""" + PairwiseQuadraticRegularizer + +A convenience constructor for creating a PairwiseQuadraticRegularizer for the +trajectory component `name` with regularization strength `Rs` over the graph `graph`. +""" +function PairwiseQuadraticRegularizer( + traj::NamedTrajectory, + Rs::Union{Float64, AbstractVector{<:Float64}}, + graph::AbstractVector{<:Tuple{Symbol, Symbol}}; + kwargs... +) + if isa(Rs, Float64) + Rs = Rs * ones(length(graph)) + end + @assert all(length(graph) == length(Rs)) "Graph and Qs must have same length" + + J = NullObjective() + for (Qᵢⱼ, (symb1, symb2)) ∈ zip(Rs, graph) + # Symbols should be the same size + dim = size(traj[symb1], 1) + J += PairwiseQuadraticRegularizer( + Qᵢⱼ * ones(dim), + 1:traj.T, + symb1, + symb2, + kwargs... + ) + end + + return J +end + +function PairwiseQuadraticRegularizer( + traj::NamedTrajectory, + R::Float64, + name1::Symbol, + name2::Symbol; + kwargs... +) + return PairwiseQuadraticRegularizer( + traj, R, [(name1, name2)]; + kwargs... + ) +end + +# ============================================================================ # + +@testitem "Quadratic Regularizer Objective" begin + using LinearAlgebra + using NamedTrajectories + using ForwardDiff + include("../../test/test_utils.jl") + + T = 10 + + Z = NamedTrajectory( + (ψ̃ = randn(4, T), u = randn(2, T)), + controls=:u, + timestep=0.1, + goal=(ψ̃ = [1.0, 0.0, 0.0, 0.0],) + ) + + + J = QuadraticRegularizer(:u, Z, [1., 1.]) + + L = Z⃗ -> J.L(Z⃗, Z) + ∇L = Z⃗ -> J.∇L(Z⃗, Z) + ∂²L = Z⃗ -> J.∂²L(Z⃗, Z) + ∂²L_structure = J.∂²L_structure(Z) + + # test objective function gradient + + @test all(ForwardDiff.gradient(L, Z.datavec) .≈ ∇L(Z.datavec)) + + # test objective function hessian + shape = (Z.dim * Z.T + Z.global_dim, Z.dim * Z.T + Z.global_dim) + @test all(isapprox( + ForwardDiff.hessian(L, Z.datavec), + dense(∂²L(Z.datavec), ∂²L_structure, shape); + atol=1e-7 + )) +end + +@testitem "Quadratic Smoothness Regularizer Objective" begin + using LinearAlgebra + using NamedTrajectories + using ForwardDiff + include("../../test/test_utils.jl") + + T = 10 + + Z = NamedTrajectory( + (ψ̃ = randn(4, T), u = randn(2, T)), + controls=:u, + timestep=0.1, + goal=(ψ̃ = [1.0, 0.0, 0.0, 0.0],) + ) + + + J = QuadraticSmoothnessRegularizer(:u, Z, [1., 1.]) + + L = Z⃗ -> J.L(Z⃗, Z) + ∇L = Z⃗ -> J.∇L(Z⃗, Z) + ∂²L = Z⃗ -> J.∂²L(Z⃗, Z) + ∂²L_structure = J.∂²L_structure(Z) + + # test objective function gradient + + @test all(ForwardDiff.gradient(L, Z.datavec) .≈ ∇L(Z.datavec)) + + # test objective function hessian + shape = (Z.dim * Z.T + Z.global_dim, Z.dim * Z.T + Z.global_dim) + @test all(isapprox( + ForwardDiff.hessian(L, Z.datavec), + dense(∂²L(Z.datavec), ∂²L_structure, shape); + atol=1e-7 + )) +end diff --git a/src/objectives/unitary_infidelity_objective.jl b/src/objectives/unitary_infidelity_objective.jl new file mode 100644 index 00000000..99217595 --- /dev/null +++ b/src/objectives/unitary_infidelity_objective.jl @@ -0,0 +1,165 @@ +export UnitaryInfidelityObjective +export UnitaryFreePhaseInfidelityObjective + +### +### UnitaryInfidelityObjective +### + +""" + UnitaryInfidelityObjective + +A type of objective that measures the infidelity of a unitary operator to a target unitary operator. + +Fields: + `name`: the name of the unitary operator in the trajectory + `goal`: the target unitary operator + `Q`: a scaling factor + `eval_hessian`: whether to evaluate the Hessian + `subspace`: the subspace in which to evaluate the objective + +""" +function UnitaryInfidelityObjective(; + name::Union{Nothing,Symbol}=nothing, + goal::Union{Nothing,AbstractVector{<:Real}}=nothing, + Q::Float64=100.0, + eval_hessian::Bool=true, + subspace=nothing +) + @assert !isnothing(goal) "unitary goal name must be specified" + + loss = :UnitaryInfidelityLoss + if isnothing(subspace) + l = eval(loss)(name, goal) + else + l = eval(loss)(name, goal; subspace=subspace) + end + + params = Dict( + :type => :UnitaryInfidelityObjective, + :name => name, + :goal => goal, + :Q => Q, + :eval_hessian => eval_hessian, + :subspace => subspace + ) + + @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + return Q * l(Z⃗[slice(Z.T, Z.components[name], Z.dim)]) + end + + @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + ∇ = zeros(Z.dim * Z.T + Z.global_dim) + Ũ⃗_slice = slice(Z.T, Z.components[name], Z.dim) + Ũ⃗ = Z⃗[Ũ⃗_slice] + ∇l = l(Ũ⃗; gradient=true) + ∇[Ũ⃗_slice] = Q * ∇l + return ∇ + end + + function ∂²L_structure(Z::NamedTrajectory) + final_time_offset = index(Z.T, 0, Z.dim) + comp_start_offset = Z.components[name][1] - 1 + structure = [ + ij .+ (final_time_offset + comp_start_offset) + for ij ∈ l.∇²l_structure + ] + return structure + end + + + @views function ∂²L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory; return_moi_vals=true) + H = spzeros(Z.dim * Z.T + Z.global_dim, Z.dim * Z.T + Z.global_dim) + Ũ⃗_slice = slice(Z.T, Z.components[name], Z.dim) + H[Ũ⃗_slice, Ũ⃗_slice] = Q * l(Z⃗[Ũ⃗_slice]; hessian=true) + if return_moi_vals + Hs = [H[i,j] for (i, j) ∈ ∂²L_structure(Z)] + return Hs + else + return H + end + end + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +function UnitaryInfidelityObjective( + name::Symbol, + traj::NamedTrajectory, + Q::Float64; + subspace=nothing, + eval_hessian::Bool=true +) + return UnitaryInfidelityObjective( + name=name, + goal=traj.goal[name], + Q=Q, + subspace=subspace, + eval_hessian=eval_hessian + ) +end + +### +### UnitaryFreePhaseInfidelityObjective +### + +""" + UnitaryFreePhaseInfidelityObjective + +A type of objective that measures the infidelity of a unitary operator to a target unitary operator, +where the target unitary operator is allowed to have phases on qubit subspaces. + +Fields: + `name`: the name of the unitary operator in the trajectory + `global_name`: the name of the global phase in the trajectory + `goal`: the target unitary operator + `Q`: a scaling factor + `eval_hessian`: whether to evaluate the Hessian + `subspace`: the subspace in which to evaluate the objective + +""" +function UnitaryFreePhaseInfidelityObjective(; + name::Union{Nothing,Symbol}=nothing, + phase_name::Union{Nothing,Symbol}=nothing, + goal::Union{Nothing,AbstractVector{<:R}}=nothing, + phase_operators::Union{Nothing,AbstractVector{<:AbstractMatrix{<:Complex{R}}}}=nothing, + Q::R=1.0, + eval_hessian::Bool=false, + subspace=nothing +) where R <: Real + @assert !isnothing(goal) "unitary goal name must be specified" + @assert !isnothing(name) "unitary name must be specified" + @assert !isnothing(phase_name) "phase name must be specified" + + loss = :UnitaryFreePhaseInfidelityLoss + l = eval(loss)(goal, phase_operators; subspace=subspace) + + params = Dict( + :type => :UnitaryFreePhaseInfidelityObjective, + :name => name, + :phase_name => phase_name, + :goal => goal, + :phase_operators => phase_operators, + :Q => Q, + :eval_hessian => eval_hessian, + :subspace => subspace + ) + + @views function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + Ũ⃗ = Z⃗[slice(Z.T, Z.components[name], Z.dim)] + ϕ⃗ = Z⃗[Z.global_components[phase_name]] + return Q * l(Ũ⃗, ϕ⃗) + end + + @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + # TODO: implement analytic + ∇ = ForwardDiff.gradient(Z⃗ -> L(Z⃗, Z), Z⃗) + # println(nnz(sparse(∇))) + # println(∇[Z.global_components[global_name]]) + return ∇ + end + + ∂²L_structure(Z::NamedTrajectory) = [] + ∂²L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) = [] + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end diff --git a/src/objectives/unitary_robustness_objective.jl b/src/objectives/unitary_robustness_objective.jl new file mode 100644 index 00000000..84bf8fc2 --- /dev/null +++ b/src/objectives/unitary_robustness_objective.jl @@ -0,0 +1,270 @@ +export UnitaryRobustnessObjective +export PairwiseUnitaryRobustnessObjective + +### +### UnitaryRobustnessObjective +### + +@doc raw""" +UnitaryRobustnessObjective(; + H::::Union{OperatorType, Nothing}=nothing, + eval_hessian::Bool=false, + symb::Symbol=:Ũ⃗ +) + +Create a control objective which penalizes the sensitivity of the infidelity to the provided +operator defined in the subspace of the control dynamics, thereby realizing robust control. + +The control dynamics are +```math +U_C(a)= \prod_t \exp{-i H_C(a_t)} +``` + +In the control frame, the H operator is (proportional to) +```math +R_{Robust}(a) = \frac{1}{T \norm{H_e}_2} \sum_t U_C(a_t)^\dag H_e U_C(a_t) \Delta t +``` +where we have adjusted to a unitless expression of the operator. + +The robustness objective is +```math +R_{Robust}(a) = \frac{1}{N} \norm{R}^2_F +``` +where N is the dimension of the Hilbert space. +""" +function UnitaryRobustnessObjective(; + H_error::Union{OperatorType, Nothing}=nothing, + eval_hessian::Bool=false, + symb::Symbol=:Ũ⃗ +) + @assert !isnothing(H_error) "H_error must be specified" + + # Indices of all non-zero subspace components for iso_vec_operators + if H_error isa EmbeddedOperator + H = unembed(H_error) + subspace = get_iso_vec_subspace_indices(H_error) + else + H = H_error + subspace = 1:length(operator_to_iso_vec(H)) + end + + @views function get_timesteps(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + return map(1:Z.T) do t + if Z.timestep isa Symbol + Z⃗[slice(t, Z.components[Z.timestep], Z.dim)][1] + else + Z.timestep + end + end + end + + # Control frame + @views function rotate(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + Δts = get_timesteps(Z⃗, Z) + T = sum(Δts) + Z_comps = Z.components[symb][subspace] + R = sum( + map(1:Z.T) do t + Uₜ = iso_vec_to_operator(Z⃗[slice(t, Z_comps, Z.dim)]) + Uₜ'H*Uₜ .* Δts[t] + end + ) / norm(H) / T + return R + end + + function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + R = rotate(Z⃗, Z) + return real(tr(R'R)) / size(R, 1) + end + + @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + ∇ = zeros(eltype(Z⃗), Z.dim * Z.T + Z.global_dim) + R = rotate(Z⃗, Z) + Δts = get_timesteps(Z⃗, Z) + Z_comps = Z.components[symb][subspace] + T = sum(Δts) + units = 1 / norm(H) / T + Threads.@threads for t ∈ 1:Z.T + # State + Uₜ_slice = slice(t, Z_comps, Z.dim) + Uₜ = iso_vec_to_operator(Z⃗[Uₜ_slice]) + + # State gradient + ∇[Uₜ_slice] .= operator_to_iso_vec(2 * H * Uₜ * R * Δts[t]) * units + + # Time gradient + if Z.timestep isa Symbol + ∂R = Uₜ'H*Uₜ + ∇[slice(t, Z.components[Z.timestep], Z.dim)] .= real(tr(∂R*R + R*∂R)) * units + end + end + return ∇ / size(R, 1) + end + + # Hessian is dense (Control frame R contains sum over all unitaries). + if eval_hessian + # TODO + ∂²L = (Z⃗, Z) -> [] + ∂²L_structure = Z -> [] + else + ∂²L = nothing + ∂²L_structure = nothing + end + + params = Dict( + :type => :UnitaryRobustnessObjective, + :H_error => H_error, + :eval_hessian => eval_hessian, + :symb => symb + ) + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end + +### +### PairwiseUnitaryRobustnessObjective +### + +""" + PairwiseUnitaryRobustnessObjective(; + H1::Union{OperatorType, Nothing}=nothing, + H2_error::Union{OperatorType, Nothing}=nothing, + symb1::Symbol=:Ũ⃗1, + symb2::Symbol=:Ũ⃗2, + eval_hessian::Bool=false, + ) + +Create a control objective which penalizes the sensitivity of the infidelity to the provided operators +defined in the subspaces of the control dynamics, thereby realizing robust control. +""" +function PairwiseUnitaryRobustnessObjective(; + H1_error::Union{OperatorType, Nothing}=nothing, + H2_error::Union{OperatorType, Nothing}=nothing, + symb1::Symbol=:Ũ⃗1, + symb2::Symbol=:Ũ⃗2, + eval_hessian::Bool=false, +) + @assert !isnothing(H1_error) "H1_error must be specified" + @assert !isnothing(H2_error) "H2_error must be specified" + + if H1_error isa EmbeddedOperator + H1 = unembed(H1_error) + subspace1 = get_iso_vec_subspace_indices(H1_error) + else + H1 = H1_error + subspace1 = 1:length(operator_to_iso_vec(H1)) + end + + if H2_error isa EmbeddedOperator + H2 = unembed(H2_error) + subspace2 = get_iso_vec_subspace_indices(H2_error) + else + H2 = H2_error + subspace2 = 1:length(operator_to_iso_vec(H2)) + end + + @views function get_timesteps(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + return map(1:Z.T) do t + if Z.timestep isa Symbol + Z⃗[slice(t, Z.components[Z.timestep], Z.dim)][1] + else + Z.timestep + end + end + end + + function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + Δts = get_timesteps(Z⃗, Z) + Z1_comps = Z.components[symb1][subspace1] + Z2_comps = Z.components[symb2][subspace2] + T = sum(Δts) + R = 0.0 + for (i₁, Δt₁) ∈ enumerate(Δts) + for (i₂, Δt₂) ∈ enumerate(Δts) + # States + U1ₜ₁ = iso_vec_to_operator(Z⃗[slice(i₁, Z1_comps, Z.dim)]) + U1ₜ₂ = iso_vec_to_operator(Z⃗[slice(i₂, Z1_comps, Z.dim)]) + U2ₜ₁ = iso_vec_to_operator(Z⃗[slice(i₁, Z2_comps, Z.dim)]) + U2ₜ₂ = iso_vec_to_operator(Z⃗[slice(i₂, Z2_comps, Z.dim)]) + + # Rotating frame + rH1ₜ₁ = U1ₜ₁'H1*U1ₜ₁ + rH1ₜ₂ = U1ₜ₂'H1*U1ₜ₂ + rH2ₜ₁ = U2ₜ₁'H2*U2ₜ₁ + rH2ₜ₂ = U2ₜ₂'H2*U2ₜ₂ + + # Robustness + units = 1 / T^2 / norm(H1)^2 / norm(H2)^2 + R += real(tr(rH1ₜ₁'rH1ₜ₂) * tr(rH2ₜ₁'rH2ₜ₂) * Δt₁ * Δt₂ * units) + end + end + return R / size(H1, 1) / size(H2, 1) + end + + @views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory) + ∇ = zeros(Z.dim * Z.T + Z.global_dim) + Δts = get_timesteps(Z⃗, Z) + Z1_comps = Z.components[symb1][subspace1] + Z2_comps = Z.components[symb2][subspace2] + T = sum(Δts) + Threads.@threads for (i₁, i₂) ∈ vec(collect(Iterators.product(1:Z.T, 1:Z.T))) + # Times + Δt₁ = Δts[i₁] + Δt₂ = Δts[i₂] + + # States + U1ₜ₁_slice = slice(i₁, Z1_comps, Z.dim) + U1ₜ₂_slice = slice(i₂, Z1_comps, Z.dim) + U2ₜ₁_slice = slice(i₁, Z2_comps, Z.dim) + U2ₜ₂_slice = slice(i₂, Z2_comps, Z.dim) + U1ₜ₁ = iso_vec_to_operator(Z⃗[U1ₜ₁_slice]) + U1ₜ₂ = iso_vec_to_operator(Z⃗[U1ₜ₂_slice]) + U2ₜ₁ = iso_vec_to_operator(Z⃗[U2ₜ₁_slice]) + U2ₜ₂ = iso_vec_to_operator(Z⃗[U2ₜ₂_slice]) + + # Rotating frame + rH1ₜ₁ = U1ₜ₁'H1*U1ₜ₁ + rH1ₜ₂ = U1ₜ₂'H1*U1ₜ₂ + rH2ₜ₁ = U2ₜ₁'H2*U2ₜ₁ + rH2ₜ₂ = U2ₜ₂'H2*U2ₜ₂ + + # ∇Uiₜⱼ (assume H's are Hermitian) + units = 1 / T^2 / norm(H1)^2 / norm(H2)^2 + R1 = tr(rH1ₜ₁'rH1ₜ₂) * Δt₁ * Δt₂ * units + R2 = tr(rH2ₜ₁'rH2ₜ₂) * Δt₁ * Δt₂ * units + ∇[U1ₜ₁_slice] += operator_to_iso_vec(2 * H1 * U1ₜ₁ * rH1ₜ₂) * R2 + ∇[U1ₜ₂_slice] += operator_to_iso_vec(2 * H1 * U1ₜ₂ * rH1ₜ₁) * R2 + ∇[U2ₜ₁_slice] += operator_to_iso_vec(2 * H2 * U2ₜ₁ * rH2ₜ₂) * R1 + ∇[U2ₜ₂_slice] += operator_to_iso_vec(2 * H2 * U2ₜ₂ * rH2ₜ₁) * R1 + + # Time gradients + if Z.timestep isa Symbol + R = real(tr(rH1ₜ₁'rH1ₜ₂) * tr(rH2ₜ₁'rH2ₜ₂)) * units + ∇[slice(i₁, Z.components[Z.timestep], Z.dim)] .= R * Δt₂ + ∇[slice(i₂, Z.components[Z.timestep], Z.dim)] .= R * Δt₁ + end + end + return ∇ / size(H1, 1) / size(H2, 1) + end + + # Hessian is dense (Control frame R contains sum over all unitaries). + if eval_hessian + # TODO + ∂²L = (Z⃗, Z) -> [] + ∂²L_structure = Z -> [] + else + ∂²L = nothing + ∂²L_structure = nothing + end + + params = Dict( + :type => :PairwiseUnitaryRobustnessObjective, + :H1_error => H1_error, + :H2_error => H2_error, + :symb1 => symb1, + :symb2 => symb2, + :eval_hessian => eval_hessian + ) + + return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params]) +end \ No newline at end of file diff --git a/src/options.jl b/src/options.jl index db455e82..8a3869b9 100644 --- a/src/options.jl +++ b/src/options.jl @@ -6,6 +6,7 @@ export set! using Ipopt using Libdl +using ExponentialAction using Base: @kwdef abstract type AbstractOptions end @@ -55,16 +56,17 @@ end """ Solver settings for Quantum Collocation. -""" +""" @kwdef mutable struct PiccoloOptions <: AbstractOptions verbose::Bool = true + verbose_evaluator::Bool = false free_time::Bool = true timesteps_all_equal::Bool = true integrator::Symbol = :pade pade_order::Int = 4 eval_hessian::Bool = false jacobian_structure::Bool = true - rollout_integrator::Function = exp + rollout_integrator::Function = expv geodesic = true blas_multithreading::Bool = true build_trajectory_constraints::Bool = true diff --git a/src/plotting.jl b/src/plotting.jl index bd2d7eed..90ab1c5b 100644 --- a/src/plotting.jl +++ b/src/plotting.jl @@ -5,7 +5,7 @@ export pretty_print using NamedTrajectories -using ..QuantumUtils +using ..Isomorphisms using ..Problems @@ -44,7 +44,7 @@ function plot_unitary_populations( transformations = OrderedDict( unitary_name => [ - x -> populations(iso_vec_to_operator(x)[:, i]) + x -> abs2.(iso_vec_to_operator(x)[:, i]) for i ∈ unitary_columns ] ) diff --git a/src/problem_templates/_problem_templates.jl b/src/problem_templates/_problem_templates.jl index 29e24c83..e82e9d2e 100644 --- a/src/problem_templates/_problem_templates.jl +++ b/src/problem_templates/_problem_templates.jl @@ -1,31 +1,24 @@ module ProblemTemplates -export UnitarySmoothPulseProblem -export UnitaryMinimumTimeProblem -export UnitaryRobustnessProblem -export UnitaryDirectSumProblem -export UnitarySamplingProblem -export UnitaryBangBangProblem - -export QuantumStateSmoothPulseProblem -export QuantumStateMinimumTimeProblem - using ..QuantumSystems -using ..QuantumUtils +using ..Isomorphisms using ..EmbeddedOperators using ..DirectSums using ..Rollouts using ..TrajectoryInitialization using ..Objectives using ..Constraints +using ..Losses using ..Integrators using ..Problems using ..Options using Distributions +using TrajectoryIndexingUtils using NamedTrajectories using LinearAlgebra using SparseArrays +using ExponentialAction using JLD2 using TestItemRunner diff --git a/src/problem_templates/quantum_state_minimum_time_problem.jl b/src/problem_templates/quantum_state_minimum_time_problem.jl index 71cd8153..d3b0fcf1 100644 --- a/src/problem_templates/quantum_state_minimum_time_problem.jl +++ b/src/problem_templates/quantum_state_minimum_time_problem.jl @@ -1,3 +1,6 @@ +export QuantumStateMinimumTimeProblem + + """ QuantumStateMinimumTimeProblem diff --git a/src/problem_templates/quantum_state_smooth_pulse_problem.jl b/src/problem_templates/quantum_state_smooth_pulse_problem.jl index b769a5ea..b3fa7613 100644 --- a/src/problem_templates/quantum_state_smooth_pulse_problem.jl +++ b/src/problem_templates/quantum_state_smooth_pulse_problem.jl @@ -1,3 +1,6 @@ +export QuantumStateSmoothPulseProblem + + """ QuantumStateSmoothPulseProblem( system::AbstractQuantumSystem, @@ -122,7 +125,7 @@ function QuantumStateSmoothPulseProblem( # Integrators ψ̃_integrators = [ - QuantumStatePadeIntegrator(system, Symbol("ψ̃$i"), :a) + QuantumStatePadeIntegrator(system, Symbol("ψ̃$i"), :a, traj) for i = 1:length(ψ_inits) ] @@ -190,3 +193,39 @@ end final = fidelity(prob) @test all(final .> initial) end + +@testitem "Test quantum state smooth pulse w/ exponential integrator" begin + # System + T = 50 + Δt = 0.2 + sys = QuantumSystem(0.1 * GATES[:Z], [GATES[:X], GATES[:Y]]) + ψ_init = [1.0, 0.0] + ψ_target = [0.0, 1.0] + integrator=:exponential + + # Single initial and target states + # -------------------------------- + prob = QuantumStateSmoothPulseProblem( + sys, ψ_init, ψ_target, T, Δt; + ipopt_options=IpoptOptions(print_level=1), + piccolo_options=PiccoloOptions(verbose=false, integrator=integrator) + ) + initial = fidelity(prob) + solve!(prob, max_iter=20) + final = fidelity(prob) + @test final > initial + + # Multiple initial and target states + # ---------------------------------- + ψ_inits = [[1.0, 0.0], [0.0, 1.0]] + ψ_targets = [[0.0, 1.0], [1.0, 0.0]] + prob = QuantumStateSmoothPulseProblem( + sys, ψ_inits, ψ_targets, T, Δt; + ipopt_options=IpoptOptions(print_level=1), + piccolo_options=PiccoloOptions(verbose=false, integrator=integrator) + ) + initial = fidelity(prob) + solve!(prob, max_iter=20) + final = fidelity(prob) + @test all(final .> initial) +end diff --git a/src/problem_templates/unitary_bang_bang_problem.jl b/src/problem_templates/unitary_bang_bang_problem.jl index fdcb8c56..f3de2650 100644 --- a/src/problem_templates/unitary_bang_bang_problem.jl +++ b/src/problem_templates/unitary_bang_bang_problem.jl @@ -1,3 +1,6 @@ +export UnitaryBangBangProblem + + @doc raw""" UnitaryBangBangProblem(system::QuantumSystem, operator, T, Δt; kwargs...) @@ -33,7 +36,7 @@ TODO: Document bang-bang modification. or - `system::QuantumSystem`: the system to be controlled with -- `operator::Union{EmbeddedOperator, AbstractMatrix{<:Number}}`: the target unitary, either in the form of an `EmbeddedOperator` or a `Matrix{ComplexF64} +- `operator::OperatorType`: the target unitary, either in the form of an `EmbeddedOperator` or a `Matrix{ComplexF64} - `T::Int`: the number of timesteps - `Δt::Float64`: the (initial) time step size @@ -68,7 +71,7 @@ TODO: control modulus norm, advanced feature, needs documentation """ function UnitaryBangBangProblem( system::AbstractQuantumSystem, - operator::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + operator::OperatorType, T::Int, Δt::Union{Float64, Vector{Float64}}; ipopt_options::IpoptOptions=IpoptOptions(), @@ -87,13 +90,14 @@ function UnitaryBangBangProblem( R=1e-2, R_a::Union{Float64, Vector{Float64}}=R, R_da::Union{Float64, Vector{Float64}}=R, - R_bang_bang::Union{Float64, Vector{Float64}}=1.0, + R_bang_bang::Union{Float64, Vector{Float64}}=1e-1, leakage_suppression=false, R_leakage=1e-1, control_norm_constraint=false, control_norm_constraint_components=nothing, control_norm_R=nothing, bound_unitary=piccolo_options.integrator == :exponential, + global_data::Union{NamedTuple, Nothing}=nothing, kwargs... ) # Trajectory @@ -116,13 +120,27 @@ function UnitaryBangBangProblem( a_guess=a_guess, system=system, rollout_integrator=piccolo_options.rollout_integrator, + global_data=global_data ) end # Objective - J = UnitaryInfidelityObjective(:Ũ⃗, traj, Q; - subspace=operator isa EmbeddedOperator ? operator.subspace_indices : nothing, - ) + if isnothing(global_data) + J = UnitaryInfidelityObjective(:Ũ⃗, traj, Q; + subspace=operator isa EmbeddedOperator ? operator.subspace_indices : nothing, + ) + else + # TODO: remove hardcoded args + J = UnitaryFreePhaseInfidelityObjective( + name=:Ũ⃗, + phase_name=:ϕ, + goal=operator_to_iso_vec(operator isa EmbeddedOperator ? operator.operator : operator), + phase_operators=[GATES[:Z] for _ in eachindex(traj.global_components[:ϕ])], + Q=Q, + eval_hessian=piccolo_options.eval_hessian, + subspace=operator isa EmbeddedOperator ? operator.subspace_indices : nothing + ) + end J += QuadraticRegularizer(:a, traj, R_a) J += QuadraticRegularizer(:da, traj, R_da) @@ -131,16 +149,16 @@ function UnitaryBangBangProblem( R_bang_bang = fill(R_bang_bang, length(system.G_drives)) end J += L1Regularizer!( - constraints, :da, traj, + constraints, :da, traj, R=R_bang_bang, eval_hessian=piccolo_options.eval_hessian ) if leakage_suppression if operator isa EmbeddedOperator - leakage_indices = get_unitary_isomorphism_leakage_indices(operator) + leakage_indices = get_iso_vec_leakage_indices(operator) J += L1Regularizer!( - constraints, :Ũ⃗, traj, - R_value=R_leakage, + constraints, :Ũ⃗, traj, + R_value=R_leakage, indices=leakage_indices, eval_hessian=piccolo_options.eval_hessian ) @@ -170,10 +188,10 @@ function UnitaryBangBangProblem( # Integrators if piccolo_options.integrator == :pade unitary_integrator = - UnitaryPadeIntegrator(system, :Ũ⃗, :a; order=piccolo_options.pade_order) + UnitaryPadeIntegrator(system, :Ũ⃗, :a, traj; order=piccolo_options.pade_order) elseif piccolo_options.integrator == :exponential unitary_integrator = - UnitaryExponentialIntegrator(system, :Ũ⃗, :a) + UnitaryExponentialIntegrator(system, :Ũ⃗, :a, traj) else error("integrator must be one of (:pade, :exponential)") end @@ -225,26 +243,24 @@ end U_goal = GATES[:H] T = 51 Δt = 0.2 - + ipopt_options = IpoptOptions(print_level=1, max_iter=25) piccolo_options = PiccoloOptions(verbose=false) - + prob = UnitaryBangBangProblem( sys, U_goal, T, Δt, R_bang_bang=10., - ipopt_options=ipopt_options, piccolo_options=piccolo_options + ipopt_options=ipopt_options, piccolo_options=piccolo_options ) - + smooth_prob = UnitarySmoothPulseProblem( sys, U_goal, T, Δt, ipopt_options=ipopt_options, piccolo_options=piccolo_options ) - initial = unitary_fidelity(prob) solve!(prob) final = unitary_fidelity(prob) @test final > initial - solve!(smooth_prob) threshold = 1e-3 a_sparse = sum(prob.trajectory.da .> 5e-2) diff --git a/src/problem_templates/unitary_direct_sum_problem.jl b/src/problem_templates/unitary_direct_sum_problem.jl index 54f1df76..bb9e8665 100644 --- a/src/problem_templates/unitary_direct_sum_problem.jl +++ b/src/problem_templates/unitary_direct_sum_problem.jl @@ -1,3 +1,6 @@ +export UnitaryDirectSumProblem + + @doc """ UnitaryDirectSumProblem(probs, final_fidelity; kwargs...) @@ -103,8 +106,9 @@ function UnitaryDirectSumProblem( if drive_reset_ratio > 0 for ℓ in prob_labels a_symb, da_symb, dda_symb = add_suffix(:a, ℓ), add_suffix(:da, ℓ), add_suffix(:dda, ℓ) + n_drives = length(traj.components[a_symb]) a, da, dda = TrajectoryInitialization.initialize_controls( - length(traj.components[a_symb]), + n_drives, n_derivatives, traj.T, traj.bounds[a_symb], @@ -116,9 +120,9 @@ function UnitaryDirectSumProblem( end end - # concatenate suffix integrators + # Rebuild integrators integrators = vcat( - [add_suffix(p.integrators, ℓ) for (p, ℓ) ∈ zip(probs, prob_labels)]... + [add_suffix(p.integrators, p.system, p.trajectory, traj, ℓ) for (p, ℓ) ∈ zip(probs, prob_labels)]... ) # direct sum (used for problem saving, only) diff --git a/src/problem_templates/unitary_minimum_time_problem.jl b/src/problem_templates/unitary_minimum_time_problem.jl index b6d71922..7409c706 100644 --- a/src/problem_templates/unitary_minimum_time_problem.jl +++ b/src/problem_templates/unitary_minimum_time_problem.jl @@ -1,3 +1,6 @@ +export UnitaryMinimumTimeProblem + + @doc raw""" UnitaryMinimumTimeProblem( trajectory::NamedTrajectory, @@ -49,6 +52,7 @@ function UnitaryMinimumTimeProblem( integrators::Vector{<:AbstractIntegrator}, constraints::Vector{<:AbstractConstraint}; unitary_symbol::Symbol=:Ũ⃗, + global_symbol::Union{Nothing, Symbol}=nothing, final_fidelity::Union{Real, Nothing}=nothing, D=1.0, ipopt_options::IpoptOptions=IpoptOptions(), @@ -59,20 +63,34 @@ function UnitaryMinimumTimeProblem( @assert unitary_symbol ∈ trajectory.names if isnothing(final_fidelity) - final_fidelity = unitary_fidelity( + final_fidelity = iso_vec_unitary_fidelity( trajectory[unitary_symbol][:, end], trajectory.goal[unitary_symbol] ) end objective += MinimumTimeObjective(trajectory; D=D, eval_hessian=piccolo_options.eval_hessian) - fidelity_constraint = FinalUnitaryFidelityConstraint( - unitary_symbol, - final_fidelity, - trajectory; - subspace=subspace, - eval_hessian=piccolo_options.eval_hessian - ) + if isnothing(global_symbol) + fidelity_constraint = FinalUnitaryFidelityConstraint( + unitary_symbol, + final_fidelity, + trajectory; + subspace=subspace, + eval_hessian=piccolo_options.eval_hessian + ) + else + # TODO: remove hardcoded args + fidelity_constraint = FinalUnitaryFreePhaseFidelityConstraint( + value=final_fidelity, + state_slice=slice(trajectory.T, trajectory.components[unitary_symbol], trajectory.dim), + phase_slice=trajectory.global_components[global_symbol], + goal=trajectory.goal[unitary_symbol], + phase_operators=[GATES[:Z] for _ in eachindex(trajectory.global_components[global_symbol])], + zdim=trajectory.dim * trajectory.T + trajectory.global_dim, + subspace=subspace, + eval_hessian=piccolo_options.eval_hessian + ) + end constraints = push!(constraints, fidelity_constraint) diff --git a/src/problem_templates/unitary_robustness_problem.jl b/src/problem_templates/unitary_robustness_problem.jl index ae4f65e2..991f5d84 100644 --- a/src/problem_templates/unitary_robustness_problem.jl +++ b/src/problem_templates/unitary_robustness_problem.jl @@ -1,3 +1,6 @@ +export UnitaryRobustnessProblem + + @doc raw""" UnitaryRobustnessProblem( H_error, trajectory, system, objective, integrators, constraints; @@ -17,7 +20,7 @@ function UnitaryRobustnessProblem end function UnitaryRobustnessProblem( - H_error::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + H_error::OperatorType, trajectory::NamedTrajectory, system::QuantumSystem, objective::Objective, @@ -37,7 +40,7 @@ function UnitaryRobustnessProblem( ) end - objective += InfidelityRobustnessObjective( + objective += UnitaryRobustnessObjective( H_error=H_error, eval_hessian=piccolo_options.eval_hessian, ) @@ -63,7 +66,7 @@ function UnitaryRobustnessProblem( end function UnitaryRobustnessProblem( - H_error::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + H_error::OperatorType, prob::QuantumControlProblem; objective::Objective=get_objective(prob), constraints::AbstractVector{<:AbstractConstraint}=get_constraints(prob), @@ -125,12 +128,13 @@ end ) solve!(rob_prob, max_iter=50) - loss(Z⃗) = InfidelityRobustnessObjective(H_error=H_embed).L(Z⃗, prob.trajectory) + loss(Z⃗) = UnitaryRobustnessObjective(H_error=H_embed).L(Z⃗, prob.trajectory) # Robustness improvement over default (or small initial) + # TODO: Can this test be improved? (might fail if unlucky) after = loss(rob_prob.trajectory.datavec) before = loss(prob.trajectory.datavec) - @test (after < before) || (before < 0.01) + @test (after < before) || (before < 0.25) # TODO: Fidelity constraint approximately satisfied @test_skip isapprox(unitary_fidelity(rob_prob; subspace=U_goal.subspace_indices), 0.99, atol=0.05) diff --git a/src/problem_templates/unitary_sampling_problem.jl b/src/problem_templates/unitary_sampling_problem.jl index a080b1e1..539a2b61 100644 --- a/src/problem_templates/unitary_sampling_problem.jl +++ b/src/problem_templates/unitary_sampling_problem.jl @@ -1,3 +1,6 @@ +export UnitarySamplingProblem + + @doc raw""" UnitarySamplingProblem @@ -7,7 +10,7 @@ robust solution by including multiple systems reflecting the problem uncertainty # Arguments - `systems::AbstractVector{<:AbstractQuantumSystem}`: A vector of quantum systems. -- `operator::Union{EmbeddedOperator, AbstractMatrix{<:Number}}`: The target unitary operator. +- `operator::OperatorType`: The target unitary operator. - `T::Int`: The number of time steps. - `Δt::Union{Float64, Vector{Float64}}`: The time step size. - `system_labels::Vector{String}`: The labels for each system. @@ -42,7 +45,7 @@ robust solution by including multiple systems reflecting the problem uncertainty """ function UnitarySamplingProblem( systems::AbstractVector{<:AbstractQuantumSystem}, - operator::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + operator::OperatorType, T::Int, Δt::Union{Float64, Vector{Float64}}; system_labels=string.(1:length(systems)), @@ -115,7 +118,7 @@ function UnitarySamplingProblem( # Constraints if leakage_suppression if operator isa EmbeddedOperator - leakage_indices = get_unitary_isomorphism_leakage_indices(operator) + leakage_indices = get_iso_vec_leakage_indices(operator) for Ũ⃗_key in Ũ⃗_keys J += L1Regularizer!( constraints, Ũ⃗_key, traj, @@ -153,12 +156,12 @@ function UnitarySamplingProblem( if piccolo_options.integrator == :pade push!( unitary_integrators, - UnitaryPadeIntegrator(sys, Ũ⃗_key, :a; order=piccolo_options.pade_order) + UnitaryPadeIntegrator(sys, Ũ⃗_key, :a, traj; order=piccolo_options.pade_order) ) elseif piccolo_options.integrator == :exponential push!( unitary_integrators, - UnitaryExponentialIntegrator(sys, Ũ⃗_key, :a) + UnitaryExponentialIntegrator(sys, Ũ⃗_key, :a, traj) ) else error("integrator must be one of (:pade, :exponential)") @@ -187,7 +190,7 @@ function UnitarySamplingProblem( system::Function, distribution::Sampleable, num_samples::Int, - operator::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + operator::OperatorType, T::Int, Δt::Union{Float64, Vector{Float64}}; kwargs... @@ -240,10 +243,10 @@ end default_fids = [] for ζ in ζ_tests Ũ⃗_end = unitary_rollout(prob.trajectory.a, timesteps, systems(ζ))[:, end] - push!(fids, unitary_fidelity(Ũ⃗_end, Ũ⃗_goal)) + push!(fids, iso_vec_unitary_fidelity(Ũ⃗_end, Ũ⃗_goal)) d_Ũ⃗_end = unitary_rollout(d_prob.trajectory.a, timesteps, systems(ζ))[:, end] - push!(default_fids, unitary_fidelity(d_Ũ⃗_end, Ũ⃗_goal)) + push!(default_fids, iso_vec_unitary_fidelity(d_Ũ⃗_end, Ũ⃗_goal)) end @test sum(fids) > sum(default_fids) diff --git a/src/problem_templates/unitary_smooth_pulse_problem.jl b/src/problem_templates/unitary_smooth_pulse_problem.jl index 1c450414..3420f70a 100644 --- a/src/problem_templates/unitary_smooth_pulse_problem.jl +++ b/src/problem_templates/unitary_smooth_pulse_problem.jl @@ -1,3 +1,6 @@ +export UnitarySmoothPulseProblem + + @doc raw""" UnitarySmoothPulseProblem(system::QuantumSystem, operator, T, Δt; kwargs...) @@ -32,7 +35,7 @@ is the *infidelity* objective function, $Q$ is a weight, $R_a$, $R_{\dot{a}}$, a or - `system::QuantumSystem`: the system to be controlled with -- `operator::Union{EmbeddedOperator, AbstractMatrix{<:Number}}`: the target unitary, either in the form of an `EmbeddedOperator` or a `Matrix{ComplexF64} +- `operator::OperatorType`: the target unitary, either in the form of an `EmbeddedOperator` or a `Matrix{ComplexF64} - `T::Int`: the number of timesteps - `Δt::Float64`: the (initial) time step size @@ -69,7 +72,7 @@ TODO: control modulus norm, advanced feature, needs documentation """ function UnitarySmoothPulseProblem( system::AbstractQuantumSystem, - operator::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + operator::OperatorType, T::Int, Δt::Union{Float64, Vector{Float64}}; ipopt_options::IpoptOptions=IpoptOptions(), @@ -97,6 +100,7 @@ function UnitarySmoothPulseProblem( control_norm_constraint_components=nothing, control_norm_R=nothing, bound_unitary=piccolo_options.integrator == :exponential, + global_data::Union{NamedTuple, Nothing}=nothing, kwargs... ) # Trajectory @@ -118,13 +122,27 @@ function UnitarySmoothPulseProblem( a_guess=a_guess, system=system, rollout_integrator=piccolo_options.rollout_integrator, + global_data=global_data ) end # Objective - J = UnitaryInfidelityObjective(:Ũ⃗, traj, Q; - subspace=operator isa EmbeddedOperator ? operator.subspace_indices : nothing, - ) + if isnothing(global_data) + J = UnitaryInfidelityObjective(:Ũ⃗, traj, Q; + subspace=operator isa EmbeddedOperator ? operator.subspace_indices : nothing, + ) + else + # TODO: remove hardcoded args + J = UnitaryFreePhaseInfidelityObjective( + name=:Ũ⃗, + phase_name=:ϕ, + goal=operator_to_iso_vec(operator isa EmbeddedOperator ? operator.operator : operator), + phase_operators=[GATES[:Z] for _ in eachindex(traj.global_components[:ϕ])], + Q=Q, + eval_hessian=piccolo_options.eval_hessian, + subspace=operator isa EmbeddedOperator ? operator.subspace_indices : nothing + ) + end J += QuadraticRegularizer(:a, traj, R_a) J += QuadraticRegularizer(:da, traj, R_da) J += QuadraticRegularizer(:dda, traj, R_dda) @@ -132,7 +150,7 @@ function UnitarySmoothPulseProblem( # Constraints if leakage_suppression if operator isa EmbeddedOperator - leakage_indices = get_unitary_isomorphism_leakage_indices(operator) + leakage_indices = get_iso_vec_leakage_indices(operator) J += L1Regularizer!( constraints, :Ũ⃗, traj, R_value=R_leakage, @@ -165,10 +183,10 @@ function UnitarySmoothPulseProblem( # Integrators if piccolo_options.integrator == :pade unitary_integrator = - UnitaryPadeIntegrator(system, :Ũ⃗, :a; order=piccolo_options.pade_order) + UnitaryPadeIntegrator(system, :Ũ⃗, :a, traj; order=piccolo_options.pade_order) elseif piccolo_options.integrator == :exponential unitary_integrator = - UnitaryExponentialIntegrator(system, :Ũ⃗, :a) + UnitaryExponentialIntegrator(system, :Ũ⃗, :a, traj) else error("integrator must be one of (:pade, :exponential)") end @@ -235,6 +253,24 @@ end @test final > initial end +@testitem "Hadamard gate with exponential integrator" begin + sys = QuantumSystem(GATES[:Z], [GATES[:X], GATES[:Y]]) + U_goal = GATES[:H] + T = 51 + Δt = 0.2 + + prob = UnitarySmoothPulseProblem( + sys, U_goal, T, Δt, + ipopt_options=IpoptOptions(print_level=1), + piccolo_options=PiccoloOptions(verbose=false, integrator=:exponential, jacobian_structure=false) + ) + + initial = unitary_fidelity(prob) + solve!(prob, max_iter=20) + final = unitary_fidelity(prob) + @test final > initial +end + @testitem "EmbeddedOperator Hadamard gate" begin a = annihilate(3) sys = QuantumSystem([(a + a')/2, (a - a')/(2im)]) diff --git a/src/problems.jl b/src/problems.jl index 2be01e3c..f762144e 100644 --- a/src/problems.jl +++ b/src/problems.jl @@ -6,8 +6,6 @@ export QuantumControlProblem export set_trajectory! export update_trajectory! -export get_traj_data -export get_datavec export get_objective export get_constraints @@ -21,8 +19,11 @@ using ..Objectives using TrajectoryIndexingUtils using NamedTrajectories + +using LinearAlgebra using JLD2 using Ipopt +using TestItemRunner using MathOptInterface using LinearAlgebra const MOI = MathOptInterface @@ -40,7 +41,7 @@ after the solver terminates. """ mutable struct QuantumControlProblem <: AbstractProblem optimizer::Ipopt.Optimizer - variables::Matrix{MOI.VariableIndex} + variables::Vector{MOI.VariableIndex} system::AbstractQuantumSystem trajectory::NamedTrajectory integrators::Union{Nothing,Vector{<:AbstractIntegrator}} @@ -60,8 +61,13 @@ function QuantumControlProblem( additional_objective::Union{Nothing, Objective}=nothing, constraints::Vector{<:AbstractConstraint}=AbstractConstraint[], params::Dict{Symbol, Any}=Dict{Symbol, Any}(), + return_evaluator=false, kwargs... ) + # Save internal copy of the options to allow modification + ipopt_options = deepcopy(ipopt_options) + piccolo_options = deepcopy(piccolo_options) + if !piccolo_options.blas_multithreading BLAS.set_num_threads(1) end @@ -85,12 +91,26 @@ function QuantumControlProblem( end evaluator = PicoEvaluator( - traj, obj, dynamics, nonlinear_constraints, eval_hessian=piccolo_options.eval_hessian + traj, + obj, + dynamics, + nonlinear_constraints; + eval_hessian=piccolo_options.eval_hessian, ) + if return_evaluator + return evaluator + end + n_dynamics_constraints = dynamics.dim * (traj.T - 1) n_variables = traj.dim * traj.T + # add globabl variables to n_variables + for global_var ∈ keys(traj.global_data) + global_var_dim = length(traj.global_data[global_var]) + n_variables += global_var_dim + end + linear_constraints = LinearConstraint[con for con ∈ constraints if con isa LinearConstraint] if piccolo_options.build_trajectory_constraints @@ -114,8 +134,6 @@ function QuantumControlProblem( verbose=piccolo_options.verbose ) - variables = reshape(variables, traj.dim, traj.T) - # Container for saving constraints and objectives params = merge(kwargs, params) params[:linear_constraints] = linear_constraints @@ -141,9 +159,14 @@ function QuantumControlProblem( traj::NamedTrajectory, obj::Objective, integrators::Vector{<:AbstractIntegrator}; + ipopt_options::IpoptOptions=IpoptOptions(), piccolo_options::PiccoloOptions=PiccoloOptions(), kwargs... ) + # Save internal copy of the options to allow modification + ipopt_options = deepcopy(ipopt_options) + piccolo_options = deepcopy(piccolo_options) + if piccolo_options.verbose println(" building dynamics from integrators...") end @@ -157,6 +180,7 @@ function QuantumControlProblem( traj, obj, dynamics; + ipopt_options=ipopt_options, piccolo_options=piccolo_options, kwargs... ) @@ -168,9 +192,14 @@ function QuantumControlProblem( traj::NamedTrajectory, obj::Objective, integrator::AbstractIntegrator; + ipopt_options::IpoptOptions=IpoptOptions(), piccolo_options::PiccoloOptions=PiccoloOptions(), kwargs... ) + # Save internal copy of the options to allow modification + ipopt_options = deepcopy(ipopt_options) + piccolo_options = deepcopy(piccolo_options) + if piccolo_options.verbose println(" building dynamics from integrator...") end @@ -184,6 +213,7 @@ function QuantumControlProblem( traj, obj, dynamics; + ipopt_options=ipopt_options, piccolo_options=piccolo_options, kwargs... ) @@ -194,9 +224,14 @@ function QuantumControlProblem( traj::NamedTrajectory, obj::Objective, f::Function; + ipopt_options::IpoptOptions=IpoptOptions(), piccolo_options::PiccoloOptions=PiccoloOptions(), kwargs... ) + # Save internal copy of the options to allow modification + ipopt_options = deepcopy(ipopt_options) + piccolo_options = deepcopy(piccolo_options) + if piccolo_options.verbose println(" building dynamics from function...") end @@ -210,6 +245,7 @@ function QuantumControlProblem( traj, obj, dynamics; + ipopt_options=ipopt_options, piccolo_options=piccolo_options, kwargs... ) @@ -262,37 +298,73 @@ function set_trajectory!( prob::QuantumControlProblem, traj::NamedTrajectory ) + # initialize n variables with trajectory data + n_vars = traj.dim * traj.T + + # set trajectory data MOI.set( prob.optimizer, MOI.VariablePrimalStart(), - vec(prob.variables), + prob.variables[1:n_vars], collect(traj.datavec) ) + + # set global variables + for global_vars_i ∈ values(traj.global_data) + n_global_vars = length(global_vars_i) + MOI.set( + prob.optimizer, + MOI.VariablePrimalStart(), + prob.variables[n_vars .+ (1:n_global_vars)], + global_vars_i + ) + n_vars += n_global_vars + end end set_trajectory!(prob::QuantumControlProblem) = set_trajectory!(prob, prob.trajectory) function get_datavec(prob::QuantumControlProblem) - Z⃗ = MOI.get( + n_vars = prob.trajectory.dim * prob.trajectory.T + + # get trajectory data + return MOI.get( prob.optimizer, MOI.VariablePrimal(), - vec(prob.variables) + prob.variables[1:n_vars] ) - return Z⃗ +end + +function get_global_data(prob::QuantumControlProblem) + + # get global variables after trajectory data + global_keys = keys(prob.trajectory.global_data) + global_values = [] + n_vars = prob.trajectory.dim * prob.trajectory.T + for global_var ∈ global_keys + n_global_vars = length(prob.trajectory.global_data[global_var]) + push!(global_values, MOI.get( + prob.optimizer, + MOI.VariablePrimal(), + prob.variables[n_vars .+ (1:n_global_vars)] + )) + n_vars += n_global_vars + end + return (; (global_keys .=> global_values)...) end @views function update_trajectory!(prob::QuantumControlProblem) - Z⃗ = get_datavec(prob) - prob.trajectory = NamedTrajectory(Z⃗, prob.trajectory) + datavec = get_datavec(prob) + global_data = get_global_data(prob) + prob.trajectory = NamedTrajectory(datavec, global_data, prob.trajectory) + return nothing end """ get_objective(prob::QuantumControlProblem) Return the objective function of the `prob::QuantumControlProblem`. - -TODO: Is deepcopy necessary? """ function get_objective( prob::QuantumControlProblem; @@ -315,8 +387,6 @@ end get_constraints(prob::QuantumControlProblem) Return the constraints of the `prob::QuantumControlProblem`. - -TODO: Is deepcopy necessary? """ function get_constraints(prob::QuantumControlProblem) linear_constraints = deepcopy(prob.params[:linear_constraints]) @@ -327,4 +397,39 @@ function get_constraints(prob::QuantumControlProblem) ] end +# ============================================================================= # + +@testitem "Additional Objective" begin + H_drift = GATES[:Z] + H_drives = [GATES[:X], GATES[:Y]] + U_goal = GATES[:H] + T = 50 + Δt = 0.2 + + prob_vanilla = UnitarySmoothPulseProblem( + H_drift, H_drives, U_goal, T, Δt, + ipopt_options=IpoptOptions(print_level=1), + piccolo_options=PiccoloOptions(verbose=false), + ) + + J_extra = QuadraticSmoothnessRegularizer(:dda, prob_vanilla.trajectory, 10.0) + + prob_additional = UnitarySmoothPulseProblem( + H_drift, H_drives, U_goal, T, Δt, + ipopt_options=IpoptOptions(print_level=1), + piccolo_options=PiccoloOptions(verbose=false), + additional_objective=J_extra, + ) + + J_prob_vanilla = Problems.get_objective(prob_vanilla) + + J_additional = Problems.get_objective(prob_additional) + + Z = prob_vanilla.trajectory + Z⃗ = vec(prob_vanilla.trajectory) + + @test J_prob_vanilla.L(Z⃗, Z) + J_extra.L(Z⃗, Z) ≈ J_additional.L(Z⃗, Z) + +end + end diff --git a/src/quantum_object_utils.jl b/src/quantum_object_utils.jl new file mode 100644 index 00000000..08f12ec3 --- /dev/null +++ b/src/quantum_object_utils.jl @@ -0,0 +1,284 @@ +module QuantumObjectUtils + +export PAULIS +export GATES +export ⊗ +export operator_from_string +export ket_from_string +export ket_from_bitstring + +export haar_random +export haar_identity + +export create +export annihilate + +using LinearAlgebra +using TestItemRunner + +# TODO: +# [ ] Remove need for oscillator operators (used by tests) +# [ ] Allow multi-character symbols for operators_from_string +# [ ] Remove need for otimes symbol or avoid import conflicts with other packages + + +@doc raw""" +A constant dictionary `GATES` containing common quantum gate matrices as complex-valued matrices. Each gate is represented by its unitary matrix. + +- `GATES[:I]` - Identity gate: Leaves the state unchanged. +- `GATES[:X]` - Pauli-X (NOT) gate: Flips the qubit state. +- `GATES[:Y]` - Pauli-Y gate: Rotates the qubit state around the Y-axis of the Bloch sphere. +- `GATES[:Z]` - Pauli-Z gate: Flips the phase of the qubit state. +- `GATES[:H]` - Hadamard gate: Creates superposition by transforming basis states. +- `GATES[:CX]` - Controlled-X (CNOT) gate: Flips the second qubit (target) if the first qubit (control) is |1⟩. +- `GATES[:CZ]` - Controlled-Z (CZ) gate: Flips the phase of the second qubit (target) if the first qubit (control) is |1⟩. +- `GATES[:XI]` - Complex gate: A specific gate used for complex operations. +- `GATES[:sqrtiSWAP]` - Square root of iSWAP gate: Partially swaps two qubits with a phase. + +```julia +julia> GATES[:Z] +2×2 Matrix{ComplexF64}: + 1.0+0.0im 0.0+0.0im + 0.0+0.0im -1.0+0.0im + +julia> get_gate(:CX) +4×4 Matrix{ComplexF64}: + 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 1.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.0+0.0im + 0.0+0.0im 0.0+0.0im 1.0+0.0im 0.0+0.0im +``` +""" +const GATES = Dict{Symbol, Matrix{ComplexF64}}( + :I => [1 0; + 0 1], + + :X => [0 1; + 1 0], + + :Y => [0 -im; + im 0], + + :Z => [1 0; + 0 -1], + + :H => [1 1; + 1 -1]/√2, + + :CX => [1 0 0 0; + 0 1 0 0; + 0 0 0 1; + 0 0 1 0], + + :CZ => [1 0 0 0; + 0 1 0 0; + 0 0 1 0; + 0 0 0 -1], + + :XI => [0 0 -im 0; + 0 0 0 -im; + -im 0 0 0; + 0 -im 0 0], + + :sqrtiSWAP => [1 0 0 0; + 0 1/sqrt(2) 1im/sqrt(2) 0; + 0 1im/sqrt(2) 1/sqrt(2) 0; + 0 0 0 1] +) + +const PAULIS = Dict{Symbol, Matrix{ComplexF64}}( + :I => GATES[:I], + :X => GATES[:X], + :Y => GATES[:Y], + :Z => GATES[:Z] +) + +### +### Kronecker product utilities +### + +@doc raw""" + ⊗(A::AbstractVecOrMat, B::AbstractVecOrMat) = kron(A, B) + +The Kronecker product, denoted by `⊗`, results in a block matrix formed by multiplying each element of `A` by the entire matrix `B`. + +```julia +julia> GATES[:X] ⊗ GATES[:Y] +4×4 Matrix{ComplexF64}: + 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 +``` +""" +⊗(A::AbstractVecOrMat, B::AbstractVecOrMat) = kron(A, B) + +@doc raw""" +operator_from_string(operator::String; lookup::Dict{Symbol, AbstractMatrix}=PAULIS) + + Reduce the string (each character is one key) via operators from a dictionary. + +""" +function operator_from_string( + operator::String; + lookup::Dict{Symbol, <:AbstractMatrix}=PAULIS +) + # TODO: allow multi-character keys, ['(', ')'] + + # split string into keys and replace with operators + characters = [Symbol(c) for c ∈ operator] + operators = replace(characters, lookup...) + + return Matrix{ComplexF64}(foldr(kron, operators)) +end + +function cavity_state(state::Int, levels::Int) + @assert state ≤ levels - 1 "Level $state is not allowed for $levels levels" + ket = zeros(levels) + ket[state + 1] = 1 + return ket +end + +@doc raw""" + ket_from_string( + ket::String, + levels::Vector{Int}; + level_dict=Dict(:g => 0, :e => 1, :f => 2, :h => 2), + return_states=false + ) + +Construct a quantum state from a string ket representation. + +# Example + +# TODO: add example +""" +function ket_from_string( + ket::String, + levels::Vector{Int}; + level_dict=Dict(:g => 0, :e => 1, :f => 2, :h => 2), + return_states=false +) + kets = [] + + for x ∈ split(ket, ['(', ')']) + if x == "" + continue + elseif all(Symbol(xᵢ) ∈ keys(level_dict) for xᵢ ∈ x) + append!(kets, x) + elseif occursin("+", x) + superposition = split(x, '+') + @assert all(all(Symbol(xᵢ) ∈ keys(level_dict) for xᵢ ∈ x) for x ∈ superposition) "Invalid ket: $x" + @assert length(superposition) == 2 "Only two states can be superposed for now" + push!(kets, x) + else + error("Invalid ket: $x") + end + end + + states = [] + + for (ψᵢ, l) ∈ zip(kets, levels) + if ψᵢ isa AbstractString && occursin("+", ψᵢ) + superposition = split(ψᵢ, '+') + superposition_states = [level_dict[Symbol(x)] for x ∈ superposition] + @assert all(state ≤ l - 1 for state ∈ superposition_states) "Level $ψᵢ is not allowed for $l levels" + superposition_state = sum([ + cavity_state(state, l) for state ∈ superposition_states + ]) + normalize!(superposition_state) + push!(states, superposition_state) + else + state = level_dict[Symbol(ψᵢ)] + @assert state ≤ l - 1 "Level $ψᵢ is not allowed for $l levels" + push!(states, cavity_state(state, l)) + end + end + + if return_states + return states + else + return kron([1.0], states...) + end +end + +@doc raw""" + ket_from_bitstring(ket::String) + +Get the state vector for a qubit system given a ket string `ket` of 0s and 1s. +""" +function ket_from_bitstring(ket::String) + cs = [c for c ∈ ket] + @assert all(c ∈ "01" for c ∈ cs) + states = [c == '0' ? [1, 0] : [0, 1] for c ∈ cs] + return Vector{ComplexF64}(foldr(kron, states)) +end + +### +### Random operators +### + +@doc raw""" + haar_random(n::Int) + +Generate a random unitary matrix using the Haar measure for an `n`-dimensional system. +""" +function haar_random(n::Int) + # Ginibre matrix + Z = (randn(n, n) + im * randn(n, n)) / √2 + F = qr(Z) + # QR correction (R main diagonal is real, strictly positive) + Λ = diagm(diag(F.R) ./ abs.(diag(F.R))) + return F.Q * Λ +end + +@doc raw""" + haar_identity(n::Int, radius::Number) + +Generate a random unitary matrix close to the identity matrix using the Haar measure for an `n`-dimensional system with a given `radius`. +""" +function haar_identity(n::Int, radius::Number) + # Ginibre matrix + Z = (I + radius * (randn(n, n) + im * randn(n, n)) / √2) / (1 + radius) + F = qr(Z) + # QR correction (R main diagonal is real, strictly positive) + Λ = diagm(diag(F.R) ./ abs.(diag(F.R))) + return F.Q * Λ +end + +### +### Oscillator operators +### + +@doc raw""" + annihilate(levels::Int) + +Get the annihilation operator for a system with `levels` levels. +""" +function annihilate(levels::Int)::Matrix{ComplexF64} + return diagm(1 => map(sqrt, 1:levels - 1)) +end + +@doc raw""" + create(levels::Int) + +Get the creation operator for a system with `levels` levels. +""" +function create(levels::Int) + return collect(annihilate(levels)') +end + +# ============================================================================= # + +@testitem "Test ket_from_bitstring function" begin + using LinearAlgebra + @test ket_from_bitstring("0") == [1, 0] + @test ket_from_bitstring("1") == [0, 1] + @test ket_from_bitstring("00") == [1, 0, 0, 0] + @test ket_from_bitstring("01") == [0, 1, 0, 0] + @test ket_from_bitstring("10") == [0, 0, 1, 0] + @test ket_from_bitstring("11") == [0, 0, 0, 1] +end + + +end diff --git a/src/quantum_system_templates/_quantum_system_templates.jl b/src/quantum_system_templates/_quantum_system_templates.jl index 9080dc18..2dbac44d 100644 --- a/src/quantum_system_templates/_quantum_system_templates.jl +++ b/src/quantum_system_templates/_quantum_system_templates.jl @@ -6,14 +6,14 @@ export MultiTransmonSystem export RydbergChainSystem export QuantumOpticsSystem -using ..QuantumUtils +using ..Isomorphisms using ..QuantumSystems +using ..QuantumObjectUtils using LinearAlgebra using TestItemRunner include("transmons.jl") include("rydberg.jl") -include("quantum_optics.jl") end diff --git a/src/quantum_system_templates/quantum_optics.jl b/src/quantum_system_templates/quantum_optics.jl deleted file mode 100644 index 01199d27..00000000 --- a/src/quantum_system_templates/quantum_optics.jl +++ /dev/null @@ -1,45 +0,0 @@ -using QuantumOpticsBase -using QuantumToolbox - -function QuantumOpticsSystem( - # Op_drift::QuantumObject{SparseMatrixCSC{ComplexF64, Int64}, OperatorQuantumObject}, - # Op_drives::Vector{QuantumObject{SparseMatrixCSC{ComplexF64, Int64}, OperatorQuantumObject}}; - Op_drift::QuantumOpticsBase.Operator{B, B, S} - where {B<:Basis, S<:SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, - Op_drives::Vector{QuantumOpticsBase.Operator{B, B, S}} - where {B<:Basis, S<:SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}; -) - # Check for Hermitian matrices - @assert QuantumToolbox.ishermitian(Op_drift) "Non-Hermitian Hamiltonian provided." - @assert all([ - QuantumToolbox.ishermitian(Op_drive) for Op_drive in Op_drives - ]) "Non-Hermitian Hamiltonian provided." - - # Extract matrices - H_drift::SparseMatrixCSC{ComplexF64, Int64} = Op_drift.data - H_drives::Vector{SparseMatrixCSC{ComplexF64, Int64}} = [Op_drive.data for Op_drive in Op_drives] - - return QuantumSystem( - H_drift, - H_drives; - constructor=QuantumOpticsSystem, - ) -end - -# ******************************************************************************* # - -@testitem "Quantum Optics System" begin - using QuantumOpticsBase - N = rand(1:5); - T = ComplexF64; - b = FockBasis(N); - a = QuantumOpticsBase.create(T, b); - H = a + a'; - sys = QuantumOpticsSystem(H, [H, H]); - @test typeof(sys) == QuantumSystem - @test sys.constructor == QuantumOpticsSystem - @test sys.H_drift == H.data - - # creation with non-Hermitian operators - @test_broken QuantumOpticsSystem(a, [a]) -end diff --git a/src/quantum_system_templates/rydberg.jl b/src/quantum_system_templates/rydberg.jl index c6c40120..e73efbdf 100644 --- a/src/quantum_system_templates/rydberg.jl +++ b/src/quantum_system_templates/rydberg.jl @@ -65,7 +65,7 @@ function RydbergChainSystem(; H_drift = zeros(ComplexF64, 2^N, 2^N) for gap in 0:N-2 for i in 1:N-gap-1 - H_drift += C * kron_from_dict( + H_drift += C * operator_from_string( generate_pattern_with_gap(N, i, gap), PAULIS ) / ((gap + 1) * distance)^6 @@ -73,10 +73,10 @@ function RydbergChainSystem(; end else if cutoff_order == 1 - H_drift = sum([C*kron_from_dict(generate_pattern(N,i),PAULIS)/(distance^6) for i in 1:N-1]) + H_drift = sum([C*operator_from_string(generate_pattern(N,i),PAULIS)/(distance^6) for i in 1:N-1]) elseif cutoff_order == 2 - H_drift = sum([C*kron_from_dict(generate_pattern(N,i),PAULIS)/(distance^6) for i in 1:N-1]) - H_drift += sum([C*kron_from_dict(generate_pattern_with_gap(N,i,1),PAULIS)/((2*distance)^6) for i in 1:N-2]) + H_drift = sum([C*operator_from_string(generate_pattern(N,i),PAULIS)/(distance^6) for i in 1:N-1]) + H_drift += sum([C*operator_from_string(generate_pattern_with_gap(N,i,1),PAULIS)/((2*distance)^6) for i in 1:N-2]) else error("Higher cutoff order not supported") end @@ -85,17 +85,17 @@ function RydbergChainSystem(; H_drives = Matrix{ComplexF64}[] # Add global X drive - Hx = sum([0.5*kron_from_dict(lift('X',i,N), PAULIS) for i in 1:N]) + Hx = sum([0.5*operator_from_string(lift('X',i,N), PAULIS) for i in 1:N]) push!(H_drives, Hx) if !ignore_Y_drive # Add global Y drive - Hy = sum([0.5*kron_from_dict(lift('Y',i,N), PAULIS) for i in 1:N]) + Hy = sum([0.5*operator_from_string(lift('Y',i,N), PAULIS) for i in 1:N]) push!(H_drives, Hy) end # Add global detuning - H_detune = -sum([kron_from_dict(lift('n',i,N), PAULIS) for i in 1:N]) + H_detune = -sum([operator_from_string(lift('n',i,N), PAULIS) for i in 1:N]) push!(H_drives, H_detune) params = Dict{Symbol, Any}( diff --git a/src/quantum_system_utils.jl b/src/quantum_system_utils.jl index 8c6e1eb5..13d71b82 100644 --- a/src/quantum_system_utils.jl +++ b/src/quantum_system_utils.jl @@ -4,10 +4,12 @@ export operator_algebra export is_reachable using ..EmbeddedOperators +using ..QuantumObjectUtils using ..QuantumSystems using LinearAlgebra using SparseArrays +using TestItemRunner commutator(A::AbstractMatrix, B::AbstractMatrix) = A * B - B * A @@ -76,8 +78,8 @@ operator_algebra(generators; return_layers=false, normalize=false, verbose=false - `remove_trace::Bool=true`: remove trace from generators """ function operator_algebra( - generators::Vector{<:AbstractMatrix{T}}; - return_layers=false, + generators::Vector{<:AbstractMatrix{T}}; + return_layers=false, normalize=false, verbose=false, remove_trace=true @@ -131,7 +133,9 @@ function operator_algebra( end if isempty(layer) - println("Subspace termination.") + if verbose + println("Subspace termination.") + end break else current_layer = layer @@ -166,11 +170,11 @@ end function is_in_span( gen::AbstractMatrix, basis::AbstractVector{<:AbstractMatrix}; - subspace_indices::AbstractVector{<:Int}=1:size(gen, 1), + subspace::AbstractVector{<:Int}=1:size(gen, 1), atol=eps(Float32), return_effective_gen=false, ) - g_basis = [deepcopy(b[subspace_indices, subspace_indices]) for b ∈ basis] + g_basis = [deepcopy(b[subspace, subspace]) for b ∈ basis] linearly_independent_subset!(g_basis) # Traceless basis needs traceless fit x = fit_gen_to_basis(gen, g_basis) @@ -184,25 +188,32 @@ function is_in_span( end """ -is_reachable(hamiltonians, gate, subspace_levels, levels; kwargs...) + is_reachable(gate, hamiltonians; kwargs...) - Check if the gate is reachable from the given generators. +Check if the gate is reachable from the given generators. If subspace_indices are provided, +then the gate should be given in the subspace. - # Arguments - - `hamiltonians::AbstractVector{<:AbstractMatrix}`: generators of the Lie algebra - - `gate::AbstractMatrix`: target gate - - `subspace_levels::Vector{<:Int}`: levels of the subspace - - `levels::Vector{<:Int}`: levels of the system +# Arguments +- `gate::AbstractMatrix`: target gate +- `hamiltonians::AbstractVector{<:AbstractMatrix}`: generators of the Lie algebra + +# Keyword Arguments +- `subspace::AbstractVector{<:Int}=1:size(gate, 1)`: subspace indices +- `compute_basis::Bool=true`: compute the basis +- `remove_trace::Bool=true`: remove trace from generators +- `verbose::Bool=false`: print debug information +- `atol::Float32=eps(Float32)`: absolute tolerance """ function is_reachable( gate::AbstractMatrix, hamiltonians::AbstractVector{<:AbstractMatrix}; - subspace_indices::AbstractVector{<:Int}=1:size(gate, 1), + subspace::AbstractVector{<:Int}=1:size(gate, 1), compute_basis=true, remove_trace=true, verbose=false, atol=eps(Float32) ) + @assert size(gate, 1) == length(subspace) "Gate must be given in the subspace." generator = im * log(gate) if remove_trace @@ -218,7 +229,7 @@ function is_reachable( return is_in_span( generator, basis, - subspace_indices=subspace_indices, + subspace=subspace, atol=atol ) end @@ -237,5 +248,126 @@ function is_reachable( return is_reachable(gate, hamiltonians; kwargs...) end +function is_reachable( + gate::EmbeddedOperator, + system::QuantumSystem; + kwargs... +) + return is_reachable( + unembed(gate), + system; + subspace=gate.subspace_indices, + kwargs... + ) +end -end \ No newline at end of file +# ============================================================================= # + +@testitem "Lie algebra basis" begin + # Check 1 qubit with complete basis + gen = operator_from_string.(["X", "Y"]) + basis = operator_algebra(gen, return_layers=false, verbose=false) + @test length(basis) == size(first(gen), 1)^2-1 + + # Check 1 qubit with complete basis and layers + basis, layers = operator_algebra(gen, return_layers=true, verbose=false) + @test length(basis) == size(first(gen), 1)^2-1 + + # Check 1 qubit with subspace + gen = operator_from_string.(["X"]) + basis = operator_algebra(gen, verbose=false) + @test length(basis) == 1 + + # Check 2 qubit with complete basis + gen = operator_from_string.(["XX", "YY", "XI", "YI", "IY", "IX"]) + basis = operator_algebra(gen, verbose=false) + @test length(basis) == size(first(gen), 1)^2-1 + + # Check 2 qubit with linearly dependent basis + gen = operator_from_string.(["XX", "YY", "XI", "XI", "IY", "IX"]) + basis = operator_algebra(gen, verbose=false) + @test length(basis) == length(gen) + + # Check 2 qubit with pair of 1-qubit subspaces + gen = operator_from_string.(["XI", "YI", "IY", "IX"]) + basis = operator_algebra(gen, verbose=false) + @test length(basis) == 2 * (2^2 - 1) +end + + +@testitem "Lie Algebra reachability" begin + using LinearAlgebra + + H_ops = Dict( + "X" => GATES[:X], + "Y" => GATES[:Y], + "Z" => GATES[:Z] + ) + + # Check 1 qubit with complete basis + gen = operator_from_string.(["X", "Y"]) + target = H_ops["Z"] + @test is_reachable(target, gen, compute_basis=true, verbose=false) + + # System + sys = QuantumSystem([GATES[:X], GATES[:Y], GATES[:Z]]) + target = GATES[:Z] + @test is_reachable(target, sys) + + # System with drift + sys = QuantumSystem(GATES[:Z], [GATES[:X]]) + target = GATES[:Z] + @test is_reachable(target, sys) + + # Check 2 qubit with complete basis + XI = GATES[:X] ⊗ GATES[:I] + IX = GATES[:I] ⊗ GATES[:X] + YI = GATES[:Y] ⊗ GATES[:I] + IY = GATES[:I] ⊗ GATES[:Y] + XX = GATES[:X] ⊗ GATES[:X] + YY = GATES[:Y] ⊗ GATES[:Y] + ZI = GATES[:Z] ⊗ GATES[:I] + IZ = GATES[:I] ⊗ GATES[:Z] + ZZ = GATES[:Z] ⊗ GATES[:Z] + + complete_gen = [XX+YY, XI, YI, IX, IY] + incomplete_gen = [XI, ZZ] + r = [0, 1, 2, 3, 4] + r /= norm(r) + R2 = exp(-im * sum([θ * H for (H, θ) in zip(complete_gen, r)])) + CZ = [1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 -1] + CX = [1 0 0 0; 0 1 0 0; 0 0 0 1; 0 0 1 0] + + # Pass + @test is_reachable(R2, complete_gen) + @test is_reachable(CZ, complete_gen) + @test is_reachable(CX, complete_gen) + @test is_reachable(XI, complete_gen) + + # Mostly fail + @test !is_reachable(R2, incomplete_gen) + @test !is_reachable(CZ, incomplete_gen) + @test !is_reachable(CX, incomplete_gen) + @test is_reachable(XI, incomplete_gen) + + # QuantumSystems + complete_gen_sys = QuantumSystem(complete_gen) + incomplete_gen_sys = QuantumSystem(incomplete_gen) + # Pass + @test is_reachable(R2, complete_gen_sys) + @test is_reachable(CZ, complete_gen_sys) + @test is_reachable(CX, complete_gen_sys) + @test is_reachable(XI, complete_gen_sys) + + # Mostly fail + @test !is_reachable(R2, incomplete_gen_sys) + @test !is_reachable(CZ, incomplete_gen_sys) + @test !is_reachable(CX, incomplete_gen_sys) + @test is_reachable(XI, incomplete_gen_sys) +end + +@testitem "Lie Algebra subspace reachability" begin + # TODO: implement tests +end + +end diff --git a/src/quantum_systems.jl b/src/quantum_systems.jl index 116120e3..fe52c2a3 100644 --- a/src/quantum_systems.jl +++ b/src/quantum_systems.jl @@ -6,70 +6,42 @@ export CompositeQuantumSystem export QuantumSystemCoupling export iso +export lift -using ..QuantumUtils +using ..Isomorphisms +using ..QuantumObjectUtils using LinearAlgebra using SparseArrays +using TestItemRunner -const Im2 = [ - 0 -1; - 1 0 -] +# TODO: +# [ ] subtypes? SingleQubitSystem, TwoQubitSystem, TransmonSystem, MultimodeSystem, etc. +# [ ] add frame info to type +# [ ] add methods to combine composite quantum systems -@doc raw""" - G(H::AbstractMatrix)::Matrix{Float64} - -Returns the isomorphism of ``-iH``: - -```math -G(H) = \widetilde{- i H} = \mqty(1 & 0 \\ 0 & 1) \otimes \Im(H) - \mqty(0 & -1 \\ 1 & 0) \otimes \Re(H) -``` - -where ``\Im(H)`` and ``\Re(H)`` are the imaginary and real parts of ``H`` and the tilde indicates the standard isomorphism of a complex valued matrix: - -```math -\widetilde{H} = \mqty(1 & 0 \\ 0 & 1) \otimes \Re(H) + \mqty(0 & -1 \\ 1 & 0) \otimes \Im(H) -``` -""" -G(H::AbstractMatrix{<:Number}) = I(2) ⊗ imag(H) - typeof(H)(Im2) ⊗ real(H) - -iso(H::AbstractMatrix{<:Number}) = I(2) ⊗ real(H) + typeof(H)(Im2) ⊗ imag(H) - - -""" - H(G::AbstractMatrix{<:Number})::Matrix{ComplexF64} - -Returns the inverse of `G(H) = iso(-iH)`, i.e. returns H - -""" -function H(G::AbstractMatrix{<:Number}) - dim = size(G, 1) ÷ 2 - H_imag = G[1:dim, 1:dim] - H_real = -G[dim+1:end, 1:dim] - return H_real + 1.0im * H_imag -end +# ----------------------------------------------------------------------------- # +# AbstractQuantumSystem +# ----------------------------------------------------------------------------- # """ ```julia AbstractQuantumSystem ``` - -Abstract type for defining systems. -""" -abstract type AbstractQuantumSystem end - -# TODO: make subtypes: SingleQubitSystem, TwoQubitSystem, TransmonSystem, MultimodeSystem, etc. - -""" -QuantumSystem | -> EmbeddedOperator | | | -> QuantumObjective | | -> Matrix (goal) - + +Abstract type for defining systems. """ +abstract type AbstractQuantumSystem end + +# ----------------------------------------------------------------------------- # +# QuantumSystem +# ----------------------------------------------------------------------------- # """ QuantumSystem <: AbstractQuantumSystem @@ -82,13 +54,12 @@ struct QuantumSystem <: AbstractQuantumSystem H_drives::Vector{SparseMatrixCSC{ComplexF64, Int}} G_drift::SparseMatrixCSC{Float64, Int} G_drives::Vector{SparseMatrixCSC{Float64, Int}} + dissipation_operators::Union{Nothing, Vector{SparseMatrixCSC{ComplexF64, Int}}} levels::Int constructor::Union{Function, Nothing} params::Dict{Symbol, <:Any} end -# TODO: add frame info to type - """ QuantumSystem( H_drift::Matrix{<:Number}, @@ -102,14 +73,15 @@ Constructs a `QuantumSystem` object from the drift and drive Hamiltonian terms. function QuantumSystem( H_drift::AbstractMatrix{<:Number}, H_drives::Vector{<:AbstractMatrix{<:Number}}; + dissipation_operators=nothing, constructor::Union{Function, Nothing}=nothing, params::Dict{Symbol, <:Any}=Dict{Symbol, Any}(), kwargs... ) H_drift = sparse(H_drift) H_drives = sparse.(H_drives) - G_drift = G(H_drift) - G_drives = G.(H_drives) + G_drift = Isomorphisms.G(H_drift) + G_drives = Isomorphisms.G.(H_drives) params = merge(params, Dict{Symbol, Any}(kwargs...)) levels = size(H_drift, 1) return QuantumSystem( @@ -117,6 +89,7 @@ function QuantumSystem( H_drives, G_drift, G_drives, + dissipation_operators, levels, constructor, params @@ -145,19 +118,20 @@ function (sys::QuantumSystem)(; params...) key ∈ keys(sys.params) for key ∈ keys(params) ]) "Invalid parameter(s) provided." return sys.constructor(; merge(sys.params, Dict(params...))...) -end +end function QuantumSystem( H_drift::SparseMatrixCSC{ComplexF64, Int64}, H_drives::Vector{SparseMatrixCSC{ComplexF64, Int64}}; + dissipation_operators=nothing, constructor::Union{Function, Nothing}=nothing, params::Dict{Symbol, <:Any}=Dict{Symbol, Any}(), kwargs... ) H_drift = sparse(H_drift) H_drives = sparse.(H_drives) - G_drift = G(H_drift) - G_drives = G.(H_drives) + G_drift = Isomorphisms.G(H_drift) + G_drives = Isomorphisms.G.(H_drives) params = merge(params, Dict{Symbol, Any}(kwargs...)) levels = size(H_drift, 1) return QuantumSystem( @@ -165,13 +139,13 @@ function QuantumSystem( H_drives, G_drift, G_drives, + dissipation_operators, levels, constructor, params ) end - function Base.copy(sys::QuantumSystem) return QuantumSystem( copy(sys.H_drift), @@ -181,12 +155,42 @@ function Base.copy(sys::QuantumSystem) ) end +# ----------------------------------------------------------------------------- # +# Quantum System couplings +# ----------------------------------------------------------------------------- # +@doc raw""" + lift(U::AbstractMatrix{<:Number}, qubit_index::Int, n_qubits::Int; levels::Int=size(U, 1)) +Lift an operator `U` acting on a single qubit to an operator acting on the entire system of `n_qubits`. +""" +function lift( + U::AbstractMatrix{<:Number}, + qubit_index::Int, + n_qubits::Int; + levels::Int=size(U, 1) +)::Matrix{ComplexF64} + Is = Matrix{Complex}[I(levels) for _ = 1:n_qubits] + Is[qubit_index] = U + return foldr(⊗, Is) +end -# ------------------------------------------------------------------ -# Quantum System couplings -# ------------------------------------------------------------------ +@doc raw""" + lift(op::AbstractMatrix{<:Number}, i::Int, subsystem_levels::Vector{Int}) + +Lift an operator `op` acting on the i-th subsystem to an operator acting on the entire system with given subsystem levels. +""" +function lift( + op::AbstractMatrix{<:Number}, + i::Int, + subsystem_levels::Vector{Int} +)::Matrix{ComplexF64} + @assert size(op, 1) == size(op, 2) == subsystem_levels[i] "Operator must be square and match dimension of subsystem i" + + Is = [collect(1.0 * typeof(op)(I, l, l)) for l ∈ subsystem_levels] + Is[i] = op + return kron(1.0, Is...) +end """ QuantumSystemCoupling <: AbstractQuantumSystem @@ -287,8 +291,8 @@ function CompositeQuantumSystem( end end - G_drift = G(H_drift) - G_drives = G.(H_drives) + G_drift = Isomorphisms.G(H_drift) + G_drives = Isomorphisms.G.(H_drives) levels = size(H_drift, 1) subsystem_levels = [sys.levels for sys ∈ subsystems] params = Dict{Symbol, Any}() @@ -400,9 +404,30 @@ function (csys::CompositeQuantumSystem)(; ) end -QuantumUtils.quantum_state(ket::String, csys::CompositeQuantumSystem; kwargs...) = - quantum_state(ket, csys.subsystem_levels; kwargs...) +# ============================================================================= # + +@testitem "System creation" begin + H_drift = GATES[:Z] + H_drives = [GATES[:X], GATES[:Y]] + n_drives = length(H_drives) + + system = QuantumSystem(H_drift, H_drives) +end + +function is_reachable( + gate::AbstractMatrix, + system::QuantumSystem; + use_drift::Bool=true, + kwargs... +) + if !iszero(system.H_drift) && use_drift + hamiltonians = [system.H_drift, system.H_drives...] + else + hamiltonians = system.H_drives + end + return is_reachable(gate, hamiltonians; kwargs...) +end + -# TODO: add methods to combine composite quantum systems end diff --git a/src/quantum_utils.jl b/src/quantum_utils.jl deleted file mode 100644 index eb47a5f0..00000000 --- a/src/quantum_utils.jl +++ /dev/null @@ -1,618 +0,0 @@ -module QuantumUtils - -export GATES -export get_gate -export ⊗ -export vec⁻¹ -export operators_from_dict -export kron_from_dict -export apply -export haar_random -export haar_identity -export qubit_system_state -export lift -export ket_to_iso -export iso_to_ket -export operator_to_iso_vec -export iso_vec_to_operator -export iso_vec_to_iso_operator -export iso_operator_to_iso_vec -export annihilate -export create -export quad -export cavity_state -export multimode_state -export number -export fidelity -export iso_fidelity -export unitary_fidelity -export population -export populations -export quantum_state - -using TrajectoryIndexingUtils -using LinearAlgebra -using SparseArrays - - -@doc raw""" - ⊗(A::AbstractVecOrMat, B::AbstractVecOrMat) = kron(A, B) - -The Kronecker product, denoted by `⊗`, results in a block matrix formed by multiplying each element of `A` by the entire matrix `B`. - -```julia -julia> GATES[:X] ⊗ GATES[:Y] -4×4 Matrix{ComplexF64}: - 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 -``` -""" -⊗(A::AbstractVecOrMat, B::AbstractVecOrMat) = kron(A, B) - - -@doc raw""" -A constant dictionary `GATES` containing common quantum gate matrices as complex-valued matrices. Each gate is represented by its unitary matrix. - -- `GATES[:I]` - Identity gate: Leaves the state unchanged. -- `GATES[:X]` - Pauli-X (NOT) gate: Flips the qubit state. -- `GATES[:Y]` - Pauli-Y gate: Rotates the qubit state around the Y-axis of the Bloch sphere. -- `GATES[:Z]` - Pauli-Z gate: Flips the phase of the qubit state. -- `GATES[:H]` - Hadamard gate: Creates superposition by transforming basis states. -- `GATES[:CX]` - Controlled-X (CNOT) gate: Flips the second qubit (target) if the first qubit (control) is |1⟩. -- `GATES[:CZ]` - Controlled-Z (CZ) gate: Flips the phase of the second qubit (target) if the first qubit (control) is |1⟩. -- `GATES[:XI]` - Complex gate: A specific gate used for complex operations. -- `GATES[:sqrtiSWAP]` - Square root of iSWAP gate: Partially swaps two qubits with a phase. - -```julia -julia> GATES[:Z] -2×2 Matrix{ComplexF64}: - 1.0+0.0im 0.0+0.0im - 0.0+0.0im -1.0+0.0im - -julia> get_gate(:CX) -4×4 Matrix{ComplexF64}: - 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 1.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.0+0.0im - 0.0+0.0im 0.0+0.0im 1.0+0.0im 0.0+0.0im -``` -""" -const GATES = Dict{Symbol, Matrix{ComplexF64}}( - :I => [1 0; - 0 1], - - :X => [0 1; - 1 0], - - :Y => [0 -im; - im 0], - - :Z => [1 0; - 0 -1], - - :H => [1 1; - 1 -1]/√2, - - :CX => [1 0 0 0; - 0 1 0 0; - 0 0 0 1; - 0 0 1 0], - - :CZ => [1 0 0 0; - 0 1 0 0; - 0 0 1 0; - 0 0 0 -1], - - :XI => [0 0 -im 0; - 0 0 0 -im; - -im 0 0 0; - 0 -im 0 0], - - :sqrtiSWAP => [1 0 0 0; - 0 1/sqrt(2) 1im/sqrt(2) 0; - 0 1im/sqrt(2) 1/sqrt(2) 0; - 0 0 0 1] -) - -get_gate(U::Symbol) = GATES[U] - -@doc raw""" - apply(gate::Symbol, ψ::Vector{<:Number}) - -Apply a quantum gate `gate` to a state vector `ψ`. -""" -function apply(gate::Symbol, ψ::Vector{<:Number}) - @assert norm(ψ) ≈ 1.0 - @assert gate in keys(GATES) "gate not found" - Û = get_gate(gate) - @assert size(Û, 2) == size(ψ, 1) "gate size does not match ket dim" - return ComplexF64.(normalize(Û * ψ)) -end - -@doc raw""" - haar_random(n::Int) - -Generate a random unitary matrix using the Haar measure for an `n`-dimensional system. -""" -function haar_random(n::Int) - # Ginibre matrix - Z = (randn(n, n) + im * randn(n, n)) / √2 - F = qr(Z) - # QR correction (R main diagonal is real, strictly positive) - Λ = diagm(diag(F.R) ./ abs.(diag(F.R))) - return F.Q * Λ -end - -@doc raw""" - haar_identity(n::Int, radius::Number) - -Generate a random unitary matrix close to the identity matrix using the Haar measure for an `n`-dimensional system with a given `radius`. -""" -function haar_identity(n::Int, radius::Number) - # Ginibre matrix - Z = (I + radius * (randn(n, n) + im * randn(n, n)) / √2) / (1 + radius) - F = qr(Z) - # QR correction (R main diagonal is real, strictly positive) - Λ = diagm(diag(F.R) ./ abs.(diag(F.R))) - return F.Q * Λ -end - -@doc raw""" -operators_from_dict(keys::AbstractVector{<:Any}, operator_dictionary; I_key=:I) - - Replace the vector of keys using the operators from a dictionary. -""" -function operators_from_dict(keys::AbstractVector{<:Any}, operator_dictionary; I_key=:I) - first_operator = first(values(operator_dictionary)) - I_default = Matrix{eltype(first_operator)}(I, size(first_operator)) - # Identity key is replaced by operator_dictionary, else default I. - return replace(replace(keys, operator_dictionary...), I_key => I_default) -end - -@doc raw""" -operators_from_dict(key_string::String, operator_dictionary; I_key="I") - - Replace the string (each character is one key) with operators from a dictionary. -""" -operators_from_dict(key_string::String, operator_dictionary; I_key="I") = - operators_from_dict([string(c) for c ∈ key_string], operator_dictionary, I_key=I_key) - -@doc raw""" -kron_from_dict(keys, dict; kwargs...) - - Reduce the keys to a single operator by using the provided dictionary and the kronecker product. -""" -function kron_from_dict(keys, dict; kwargs...) - if occursin("+", keys) - return sum( - [kron_from_dict(string(s), dict; kwargs...) - for s ∈ split(keys, "+")] - ) - else - return reduce(kron, operators_from_dict(keys, dict; kwargs...)) - end -end - -@doc raw""" - qubit_system_state(ket::String) - -Get the state vector for a qubit system given a ket string `ket` of 0s and 1s. -""" -function qubit_system_state(ket::String) - cs = [c for c ∈ ket] - @assert all(c ∈ "01" for c ∈ cs) - states = [c == '0' ? [1, 0] : [0, 1] for c ∈ cs] - ψ = foldr(⊗, states) - ψ = Vector{ComplexF64}(ψ) - return ψ -end - -@doc raw""" - lift(U::AbstractMatrix{<:Number}, qubit_index::Int, n_qubits::Int; levels::Int=size(U, 1)) - -Lift an operator `U` acting on a single qubit to an operator acting on the entire system of `n_qubits`. -""" -function lift( - U::AbstractMatrix{<:Number}, - qubit_index::Int, - n_qubits::Int; - levels::Int=size(U, 1) -)::Matrix{ComplexF64} - Is = Matrix{Complex}[I(levels) for _ = 1:n_qubits] - Is[qubit_index] = U - return foldr(⊗, Is) -end - -@doc raw""" - lift(op::AbstractMatrix{<:Number}, i::Int, subsystem_levels::Vector{Int}) - -Lift an operator `op` acting on the i-th subsystem to an operator acting on the entire system with given subsystem levels. -""" -function lift( - op::AbstractMatrix{<:Number}, - i::Int, - subsystem_levels::Vector{Int} -)::Matrix{ComplexF64} - @assert size(op, 1) == size(op, 2) == subsystem_levels[i] "Operator must be square and match dimension of subsystem i" - - Is = [collect(1.0 * typeof(op)(I, l, l)) for l ∈ subsystem_levels] - Is[i] = op - return kron(1.0, Is...) -end - - - - -""" - quantum harmonic oscillator operators -""" - -@doc raw""" - annihilate(levels::Int) - -Get the annihilation operator for a system with `levels` levels. -""" -function annihilate(levels::Int)::Matrix{ComplexF64} - return diagm(1 => map(sqrt, 1:levels - 1)) -end - -@doc raw""" - create(levels::Int) - -Get the creation operator for a system with `levels` levels. -""" -function create(levels::Int) - return collect(annihilate(levels)') -end - -@doc raw""" - number(levels::Int) - -Get the number operator `n = a'a` for a system with `levels` levels. -""" -function number(levels::Int) - return create(levels) * annihilate(levels) -end - -@doc raw""" - quad(levels::Int) - -Get the operator `n(n - I)` for a system with `levels` levels. -""" -function quad(levels::Int) - return number(levels) * (number(levels) - I(levels)) -end - -@doc raw""" - cavity_state(level::Int, cavity_levels::Int) - -Generate the state vector for a given `level` in a cavity system with `cavity_levels` levels. - -# Arguments -- `level::Int`: The index of the desired level (must be between 0 and `cavity_levels` - 1). -- `cavity_levels::Int`: The total number of levels in the cavity system. - -# Returns -- `Vector{ComplexF64}`: A state vector with `cavity_levels` elements where the element at `level + 1` is 1.0 and all other elements are 0.0. - -# Throws -- `BoundsError`: If `level` is less than 0 or greater than or equal to `cavity_levels`. - -# Examples -```julia -julia> cavity_state(2, 5) -5-element Vector{ComplexF64}: - 0.0 + 0.0im - 0.0 + 0.0im - 1.0 + 0.0im - 0.0 + 0.0im - 0.0 + 0.0im -``` -""" -function cavity_state(level::Int, cavity_levels::Int) - if level < 0 || level >= cavity_levels - throw(BoundsError("Invalid level index $level for a cavity with $cavity_levels levels.")) - end - state = zeros(ComplexF64, cavity_levels) - state[level + 1] = 1. - return state -end - - -@doc raw""" - multimode system utilities -""" - -function multimode_state(ψ::String, transmon_levels::Int, cavity_levels::Int) - @assert length(ψ) == 2 - - @assert transmon_levels ∈ 2:4 - - transmon_state = ψ[1] - - @assert transmon_state ∈ ['g', 'e'] - - cavity_state = parse(Int, ψ[2]) - - @assert cavity_state ∈ 0:cavity_levels - 2 "cavity state must be in [0, ..., cavity_levels - 2] (hightest cavity level is prohibited)" - - ψ_transmon = zeros(ComplexF64, transmon_levels) - ψ_transmon[transmon_state == 'g' ? 1 : 2] = 1.0 - - ψ_cavity = zeros(ComplexF64, cavity_levels) - ψ_cavity[cavity_state + 1] = 1.0 - - return ψ_transmon ⊗ ψ_cavity -end - - -""" - isomporphism utilities -""" - -@doc raw""" - vec⁻¹(x::AbstractVector) - -Convert a vector `x` into a square matrix. The length of `x` must be a perfect square. -""" -function vec⁻¹(x::AbstractVector) - n = isqrt(length(x)) - return reshape(x, n, n) -end - -@doc raw""" - ket_to_iso(ψ) - -Convert a ket vector `ψ` into a complex vector with real and imaginary parts. -""" -ket_to_iso(ψ) = [real(ψ); imag(ψ)] - -@doc raw""" - iso_to_ket(ψ̃) - -Convert a complex vector `ψ̃` with real and imaginary parts into a ket vector. -""" -iso_to_ket(ψ̃) = ψ̃[1:div(length(ψ̃), 2)] + im * ψ̃[(div(length(ψ̃), 2) + 1):end] - -@doc raw""" - iso_vec_to_operator(Ũ⃗::AbstractVector{R}) where R <: Real - -Convert a real vector `Ũ⃗` into a complex matrix representing an operator. -""" -function iso_vec_to_operator(Ũ⃗::AbstractVector{R}) where R <: Real - Ũ⃗_dim = div(length(Ũ⃗), 2) - N = Int(sqrt(Ũ⃗_dim)) - U = Matrix{Complex{R}}(undef, N, N) - for i=0:N-1 - U[:, i+1] .= - @view(Ũ⃗[i * 2N .+ (1:N)]) + - one(R) * im * @view(Ũ⃗[i * 2N .+ (N+1:2N)]) - end - return U -end - -@doc raw""" - iso_vec_to_iso_operator(Ũ⃗::AbstractVector{R}) where R <: Real - -Convert a real vector `Ũ⃗` into a real matrix representing an isomorphism operator. -""" -function iso_vec_to_iso_operator(Ũ⃗::AbstractVector{R}) where R <: Real - N = Int(sqrt(length(Ũ⃗) ÷ 2)) - Ũ = Matrix{R}(undef, 2N, 2N) - U_real = Matrix{R}(undef, N, N) - U_imag = Matrix{R}(undef, N, N) - for i=0:N-1 - U_real[:, i+1] .= @view(Ũ⃗[i*2N .+ (1:N)]) - U_imag[:, i+1] .= @view(Ũ⃗[i*2N .+ (N+1:2N)]) - end - Ũ[1:N, 1:N] .= U_real - Ũ[1:N, (N + 1):end] .= -U_imag - Ũ[(N + 1):end, 1:N] .= U_imag - Ũ[(N + 1):end, (N + 1):end] .= U_real - return Ũ -end - -@doc raw""" - operator_to_iso_vec(U::AbstractMatrix{<:Complex}) - -Convert a complex matrix `U` representing an operator into a real vector. -""" -function operator_to_iso_vec(U::AbstractMatrix{<:Complex}) - N = size(U,1) - Ũ⃗ = Vector{Float64}(undef, N^2 * 2) - for i=0:N-1 - Ũ⃗[i*2N .+ (1:N)] .= real(@view(U[:, i+1])) - Ũ⃗[i*2N .+ (N+1:2N)] .= imag(@view(U[:, i+1])) - end - return Ũ⃗ -end - -@doc raw""" - iso_operator_to_iso_vec(Ũ::AbstractMatrix{R}) where R <: Real - -Convert a real matrix `Ũ` representing an isomorphism operator into a real vector. -""" -function iso_operator_to_iso_vec(Ũ::AbstractMatrix{R}) where R <: Real - N = size(Ũ, 1) ÷ 2 - Ũ⃗ = Vector{R}(undef, N^2 * 2) - for i=0:N-1 - Ũ⃗[i*2N .+ (1:2N)] .= @view Ũ[:, i+1] - end - return Ũ⃗ -end - - -""" - quantum metrics -""" - -@doc raw""" - fidelity(ψ, ψ_goal; subspace=1:length(ψ)) - -Calculate the fidelity between two quantum states `ψ` and `ψ_goal`. -""" -function fidelity(ψ, ψ_goal; subspace=1:length(ψ)) - ψ = ψ[subspace] - ψ_goal = ψ_goal[subspace] - return abs2(ψ_goal' * ψ) -end - -@doc raw""" - iso_fidelity(ψ̃, ψ̃_goal; kwargs...) - -Calculate the fidelity between two quantum states in their isomorphic form `ψ̃` and `ψ̃_goal`. -""" -function iso_fidelity(ψ̃, ψ̃_goal; kwargs...) - ψ = iso_to_ket(ψ̃) - ψ_goal = iso_to_ket(ψ̃_goal) - return fidelity(ψ, ψ_goal; kwargs...) -end - -@doc raw""" - unitary_fidelity(U::Matrix, U_goal::Matrix; subspace=nothing) - -Calculate the fidelity between two unitary operators `U` and `U_goal`. -""" -function unitary_fidelity( - U::Matrix, - U_goal::Matrix; - subspace=nothing -) - if isnothing(subspace) - N = size(U, 1) - return 1 / N * abs(tr(U_goal'U)) - else - U_goal = U_goal[subspace, subspace] - U = U[subspace, subspace] - N = length(subspace) - return 1 / N * abs(tr(U_goal'U)) - end -end - -@doc raw""" - unitary_fidelity(Ũ⃗::AbstractVector{<:Real}, Ũ⃗_goal::AbstractVector{<:Real}; subspace=nothing) - -Calculate the fidelity between two unitary operators in their isomorphic form `Ũ⃗` and `Ũ⃗_goal`. -""" -function unitary_fidelity( - Ũ⃗::AbstractVector{<:Real}, - Ũ⃗_goal::AbstractVector{<:Real}; - subspace=nothing -) - U = iso_vec_to_operator(Ũ⃗) - U_goal = iso_vec_to_operator(Ũ⃗_goal) - return unitary_fidelity(U, U_goal; subspace=subspace) -end - -# TODO: add unitary squared fidelity - -""" - quantum measurement functions -""" - -@doc raw""" - population(ψ̃, i) - -Calculate the population of the i-th level for a given state vector `ψ̃` in its isomorphic form. -""" -function population(ψ̃, i) - @assert i ∈ 0:length(ψ̃) ÷ 2 - 1 - ψ = iso_to_ket(ψ̃) - return abs2(ψ[i + 1]) -end - -@doc raw""" - populations(ψ::AbstractVector{<:Complex}) - -Calculate the populations for each level of a given state vector `ψ`. -""" -function populations(ψ::AbstractVector{<:Complex}) - return abs2.(ψ) -end - -@doc raw""" - populations(ψ̃::AbstractVector{<:Real}) - -Calculate the populations for each level of a given state vector `ψ̃` in its isomorphic form. -""" -function populations(ψ̃::AbstractVector{<:Real}) - return populations(iso_to_ket(ψ̃)) -end - - -@doc raw""" - quantum_state( - ket::String, - levels::Vector{Int}; - level_dict=Dict(:g => 0, :e => 1, :f => 2, :h => 2), - return_states=false - ) - -Construct a quantum state from a string ket representation. - -# Example - -# TODO: add example -""" -function quantum_state( - ket::String, - levels::Vector{Int}; - level_dict=Dict(:g => 0, :e => 1, :f => 2, :h => 2), - return_states=false -) - kets = [] - - for x ∈ split(ket, ['(', ')']) - if x == "" - continue - elseif all(Symbol(xᵢ) ∈ keys(level_dict) for xᵢ ∈ x) - append!(kets, x) - elseif occursin("+", x) - superposition = split(x, '+') - @assert all(all(Symbol(xᵢ) ∈ keys(level_dict) for xᵢ ∈ x) for x ∈ superposition) "Invalid ket: $x" - @assert length(superposition) == 2 "Only two states can be superposed for now" - push!(kets, x) - else - error("Invalid ket: $x") - end - end - - states = [] - - for (ψᵢ, l) ∈ zip(kets, levels) - if ψᵢ isa AbstractString && occursin("+", ψᵢ) - superposition = split(ψᵢ, '+') - superposition_states = [level_dict[Symbol(x)] for x ∈ superposition] - @assert all(state ≤ l - 1 for state ∈ superposition_states) "Level $ψᵢ is not allowed for $l levels" - superposition_state = sum([ - cavity_state(state, l) for state ∈ superposition_states - ]) - normalize!(superposition_state) - push!(states, superposition_state) - else - state = level_dict[Symbol(ψᵢ)] - @assert state ≤ l - 1 "Level $ψᵢ is not allowed for $l levels" - push!(states, cavity_state(state, l)) - end - end - - if return_states - return states - else - return kron([1.0], states...) - end -end - - - - - - - - - - - -end diff --git a/src/rollouts.jl b/src/rollouts.jl index 96df6bf8..693094be 100644 --- a/src/rollouts.jl +++ b/src/rollouts.jl @@ -1,50 +1,104 @@ module Rollouts export rollout +export open_rollout export unitary_rollout export lab_frame_unitary_rollout export lab_frame_unitary_rollout_trajectory -export sample_at -export resample -using ..QuantumUtils +using ..Isomorphisms using ..QuantumSystems +using ..QuantumSystemUtils +using ..QuantumObjectUtils using ..EmbeddedOperators using ..Integrators +using ..Losses using ..Problems using ..DirectSums -using LinearAlgebra using NamedTrajectories +using ExponentialAction +using LinearAlgebra +using ProgressMeter +using TestItemRunner + + +# ----------------------------------------------------------------------------- # + +""" + infer_is_jvp(integrator::Function) + +Infer whether the integrator is a jacobian-vector product (JVP) function. + +If `true`, the integrator is expected to have a signature like the exponential action, +`expv`. Otherwise, it is expected to have a signature like `exp`. +""" +function infer_is_jvp(integrator::Function) + # name + args + ns = fieldcount.([m.sig for m ∈ methods(integrator)]) + is_exp = 2 ∈ ns + is_expv = 4 ∈ ns + if is_exp && is_expv + throw(ErrorException("Ambiguous rollout integrator signature. Please specify manually.")) + elseif is_exp + return false + elseif is_expv + return true + else + throw(ErrorException("No valid rollout integrator signature found.")) + end +end + +# ----------------------------------------------------------------------------- # +# Quantum state rollouts +# ----------------------------------------------------------------------------- # + +@doc raw""" + rollout( + ψ̃_init::AbstractVector{<:Float64}, + controls::AbstractMatrix, + Δt::AbstractVector, + system::AbstractQuantumSystem + ) + +Rollout a quantum state `ψ̃_init` under the control `controls` for a time `Δt` +using the system `system`. + +If `jacobian_vector_product` is `true`, the integrator is expected to have a signature like +the exponential action, `expv`. Otherwise, it is expected to have a signature like `exp`. + +Types should allow for autodifferentiable controls and times. +""" function rollout( - ψ̃₁::AbstractVector{Float64}, - controls::AbstractMatrix{Float64}, - Δt::Union{AbstractVector{Float64}, AbstractMatrix{Float64}, Float64}, + ψ̃_init::AbstractVector{<:Real}, + controls::AbstractMatrix, + Δt::AbstractVector, system::AbstractQuantumSystem; - integrator=exp, + show_progress=false, + integrator=expv, + jacobian_vector_product=infer_is_jvp(integrator), G=Integrators.G_bilinear ) - if Δt isa AbstractMatrix - @assert size(Δt, 1) == 1 - Δt = vec(Δt) - elseif Δt isa Float64 - Δt = fill(Δt, size(controls, 2)) - end - T = size(controls, 2) - Ψ̃ = zeros(length(ψ̃₁), T) + Ψ̃ = zeros(Real, length(ψ̃_init), T) - Ψ̃[:, 1] .= ψ̃₁ + Ψ̃[:, 1] .= ψ̃_init G_drift = Matrix{Float64}(system.G_drift) G_drives = Matrix{Float64}.(system.G_drives) + p = Progress(T-1; enabled=show_progress) for t = 2:T aₜ₋₁ = controls[:, t - 1] Gₜ = G(aₜ₋₁, G_drift, G_drives) - Ψ̃[:, t] .= integrator(Gₜ * Δt[t - 1]) * Ψ̃[:, t - 1] + if jacobian_vector_product + Ψ̃[:, t] .= integrator(Δt[t - 1], Gₜ, Ψ̃[:, t - 1]) + else + Ψ̃[:, t] .= integrator(Gₜ * Δt[t - 1]) * Ψ̃[:, t - 1] + end + next!(p) end return Ψ̃ @@ -54,35 +108,35 @@ rollout(ψ::Vector{<:Complex}, args...; kwargs...) = rollout(ket_to_iso(ψ), args...; kwargs...) function rollout( - ψ̃₁s::AbstractVector{<:AbstractVector}, args...; kwargs... + inits::AbstractVector{<:AbstractVector}, args...; kwargs... ) - return vcat([rollout(ψ̃₁, args...; kwargs...) for ψ̃₁ ∈ ψ̃₁s]...) + return vcat([rollout(state, args...; kwargs...) for state ∈ inits]...) end -function QuantumUtils.fidelity( - ψ̃₁::AbstractVector{Float64}, - ψ̃_goal::AbstractVector{Float64}, - controls::AbstractMatrix{Float64}, - Δt::Union{AbstractVector{Float64}, AbstractMatrix{Float64}, Float64}, +function Losses.iso_fidelity( + ψ̃_init::AbstractVector{<:Real}, + ψ̃_goal::AbstractVector{<:Real}, + controls::AbstractMatrix, + Δt::AbstractVector, system::AbstractQuantumSystem; kwargs... ) - Ψ̃ = rollout(ψ̃₁, controls, Δt, system; kwargs...) + Ψ̃ = rollout(ψ̃_init, controls, Δt, system; kwargs...) return iso_fidelity(Ψ̃[:, end], ψ̃_goal) end -function QuantumUtils.fidelity( - ψ₁::AbstractVector{<:Complex}, +function Losses.fidelity( + ψ_init::AbstractVector{<:Complex}, ψ_goal::AbstractVector{<:Complex}, - controls::AbstractMatrix{Float64}, - Δt::Union{AbstractVector{Float64}, AbstractMatrix{Float64}, Float64}, + controls::AbstractMatrix, + Δt::AbstractVector, system::AbstractQuantumSystem; kwargs... ) - return fidelity(ket_to_iso(ψ₁), ket_to_iso(ψ_goal), controls, Δt, system; kwargs...) + return iso_fidelity(ket_to_iso(ψ_init), ket_to_iso(ψ_goal), controls, Δt, system; kwargs...) end -function QuantumUtils.fidelity( +function Losses.fidelity( trajectory::NamedTrajectory, system::AbstractQuantumSystem; state_symb::Symbol=:ψ̃, @@ -92,76 +146,114 @@ function QuantumUtils.fidelity( fids = [] for symb in trajectory.names if startswith(symb, state_symb) + controls = trajectory[control_symb] init = trajectory.initial[symb] goal = trajectory.goal[symb] - push!( - fids, - fidelity(init, goal, trajectory[control_symb], get_timesteps(trajectory), system; kwargs...) - ) + fid = iso_fidelity(init, goal, controls, get_timesteps(trajectory), system; kwargs...) + push!(fids, fid) end end - return fids + return length(fids) == 1 ? fids[1] : fids end -function QuantumUtils.fidelity( +function Losses.fidelity( prob::QuantumControlProblem; kwargs... ) return fidelity(prob.trajectory, prob.system; kwargs...) end -# ============================================================================= # +# ----------------------------------------------------------------------------- # +# Open quantum system rollouts +# ----------------------------------------------------------------------------- # -function unitary_rollout( - Ũ⃗₁::AbstractVector{<:Real}, - controls::AbstractMatrix{Float64}, - Δt::Union{AbstractVector{Float64}, AbstractMatrix{Float64}, Float64}, +function open_rollout( + ρ⃗₁::AbstractVector{<:Complex}, + controls::AbstractMatrix, + Δt::AbstractVector, system::AbstractQuantumSystem; - integrator=exp, - G=Integrators.G_bilinear + show_progress=false, + integrator=expv, + jacobian_vector_product=infer_is_jvp(integrator), + H=a -> Integrators.G_bilinear(a, system.H_drift, system.H_drives), ) - if Δt isa AbstractMatrix - @assert size(Δt, 1) == 1 - Δt = vec(Δt) - elseif Δt isa Float64 - Δt = fill(Δt, size(controls, 2)) + T = size(controls, 2) + + ρ⃗̃ = zeros(Real, 2length(ρ⃗₁), T) + + ρ⃗̃[:, 1] .= ket_to_iso(ρ⃗₁) + + if isnothing(system.dissipation_operators) + @error "No dissipation operators found in system" + L = iso(zeros(Float64, size(system.H_drift))) + else + L_dissipators = system.dissipation_operators + L = Integrators.L_function(L_dissipators) end + p = Progress(T-1; enabled=show_progress) + for t = 2:T + aₜ₋₁ = controls[:, t - 1] + adGₜ = Isomorphisms.G(ad_vec(H(aₜ₋₁))) + if jacobian_vector_product + ρ⃗̃[:, t] = integrator(Δt[t - 1], adGₜ + iso(L), ρ⃗̃[:, t - 1]) + else + ρ⃗̃[:, t] = integrator(Δt[t - 1], adGₜ + iso(L)) * ρ⃗̃[:, t - 1] + end + next!(p) + end + + return ρ⃗̃ +end + +# ----------------------------------------------------------------------------- # +# Unitary rollouts +# ----------------------------------------------------------------------------- # + +function unitary_rollout( + Ũ⃗_init::AbstractVector{<:Real}, + controls::AbstractMatrix, + Δt::AbstractVector, + system::AbstractQuantumSystem; + show_progress=false, + integrator=expv, + jacobian_vector_product=infer_is_jvp(integrator), + G=Integrators.G_bilinear, +) T = size(controls, 2) - Ũ⃗ = zeros(length(Ũ⃗₁), T) + Ũ⃗ = zeros(Real, length(Ũ⃗_init), T) - Ũ⃗[:, 1] .= Ũ⃗₁ + Ũ⃗[:, 1] .= Ũ⃗_init G_drift = Matrix{Float64}(system.G_drift) G_drives = Matrix{Float64}.(system.G_drives) + p = Progress(T-1; enabled=show_progress) for t = 2:T aₜ₋₁ = controls[:, t - 1] Gₜ = G(aₜ₋₁, G_drift, G_drives) - Ũ⃗ₜ₋₁ = Ũ⃗[:, t - 1] - Ũₜ₋₁ = iso_vec_to_iso_operator(Ũ⃗ₜ₋₁) - Ũₜ = integrator(Gₜ * Δt[t - 1]) * Ũₜ₋₁ - Ũ⃗ₜ = iso_operator_to_iso_vec(Ũₜ) - Ũ⃗[:, t] .= Ũ⃗ₜ + Ũₜ₋₁ = iso_vec_to_iso_operator(Ũ⃗[:, t - 1]) + if jacobian_vector_product + Ũₜ = integrator(Δt[t - 1], Gₜ, Ũₜ₋₁) + else + Ũₜ = integrator(Gₜ * Δt[t - 1]) * Ũₜ₋₁ + end + Ũ⃗[:, t] .= iso_operator_to_iso_vec(Ũₜ) + next!(p) end return Ũ⃗ end function unitary_rollout( - controls::AbstractMatrix{Float64}, - Δt::Union{AbstractVector{Float64}, AbstractMatrix{Float64}, Float64}, + controls::AbstractMatrix, + Δt::AbstractVector, system::AbstractQuantumSystem; - integrator=exp + kwargs... ) - return unitary_rollout( - operator_to_iso_vec(Matrix{ComplexF64}(I(size(system.H_drift, 1)))), - controls, - Δt, - system; - integrator=integrator - ) + Ĩ⃗ = operator_to_iso_vec(Matrix{ComplexF64}(I(size(system.H_drift, 1)))) + return unitary_rollout(Ĩ⃗, controls, Δt, system; kwargs...) end function unitary_rollout( @@ -169,53 +261,94 @@ function unitary_rollout( system::AbstractQuantumSystem; unitary_name::Symbol=:Ũ⃗, drive_name::Symbol=:a, - integrator=exp, - only_drift=false + kwargs... ) - Ũ⃗₁ = traj.initial[unitary_name] - if only_drift - controls = zeros(size(traj[drive_name])) - else - controls = traj[drive_name] - end - Δt = get_timesteps(traj) return unitary_rollout( - Ũ⃗₁, - controls, - Δt, + traj.initial[unitary_name], + traj[drive_name], + get_timesteps(traj), system; - integrator=integrator + kwargs... ) end -function QuantumUtils.unitary_fidelity( +function Losses.iso_vec_unitary_fidelity( + Ũ⃗_init::AbstractVector{<:Real}, + Ũ⃗_goal::AbstractVector{<:Real}, + controls::AbstractMatrix, + Δt::AbstractVector, + system::AbstractQuantumSystem; + subspace::AbstractVector{Int}=axes(iso_vec_to_operator(Ũ⃗_goal), 1), + kwargs... +) + Ũ⃗ = unitary_rollout(Ũ⃗_init, controls, Δt, system; kwargs...) + return iso_vec_unitary_fidelity(Ũ⃗[:, end], Ũ⃗_goal; subspace=subspace) +end + +function Losses.iso_vec_unitary_fidelity( + Ũ⃗_goal::AbstractVector{<:Real}, + controls::AbstractMatrix, + Δt::AbstractVector, + system::AbstractQuantumSystem; + kwargs... +) + Ĩ⃗ = operator_to_iso_vec(Matrix{ComplexF64}(I(size(system.H_drift, 1)))) + return iso_vec_unitary_fidelity(Ĩ⃗, Ũ⃗_goal, controls, Δt, system; kwargs...) +end + +function Losses.unitary_fidelity( + U_init::AbstractMatrix{<:Complex}, + U_goal::AbstractMatrix{<:Complex}, + controls::AbstractMatrix, + Δt::AbstractVector, + system::AbstractQuantumSystem; + kwargs... +) + Ũ⃗_init = operator_to_iso_vec(U_init) + Ũ⃗_goal = operator_to_iso_vec(U_goal) + return iso_vec_unitary_fidelity(Ũ⃗_init, Ũ⃗_goal, controls, Δt, system; kwargs...) +end + +Losses.unitary_fidelity( + U_goal::AbstractMatrix{<:Complex}, + controls::AbstractMatrix, + Δt::AbstractVector, + system::AbstractQuantumSystem; + kwargs... +) = iso_vec_unitary_fidelity(operator_to_iso_vec(U_goal), controls, Δt, system; kwargs...) + +Losses.unitary_fidelity( + U_goal::EmbeddedOperator, + controls::AbstractMatrix, + Δt::AbstractVector, + system::AbstractQuantumSystem; + subspace::AbstractVector{Int}=U_goal.subspace_indices, + kwargs... +) = unitary_fidelity(U_goal.operator, controls, Δt, system; subspace=subspace, kwargs...) + +function Losses.unitary_fidelity( traj::NamedTrajectory, sys::AbstractQuantumSystem; unitary_name::Symbol=:Ũ⃗, - subspace=nothing, + drive_name::Symbol=:a, kwargs... ) - Ũ⃗_final = unitary_rollout(traj, sys; unitary_name=unitary_name, kwargs...)[:, end] - return unitary_fidelity( Ũ⃗_final, traj.goal[unitary_name]; subspace=subspace) + Ũ⃗_init = traj.initial[unitary_name] + Ũ⃗_goal = traj.goal[unitary_name] + controls = traj[drive_name] + Δt = get_timesteps(traj) + return iso_vec_unitary_fidelity(Ũ⃗_init, Ũ⃗_goal, controls, Δt, sys; kwargs...) end -QuantumUtils.unitary_fidelity(prob::QuantumControlProblem; kwargs...) = +Losses.unitary_fidelity(prob::QuantumControlProblem; kwargs...) = unitary_fidelity(prob.trajectory, prob.system; kwargs...) -function QuantumUtils.unitary_fidelity( - U_goal::AbstractMatrix{ComplexF64}, - controls::AbstractMatrix{Float64}, - Δt::Union{AbstractVector{Float64}, AbstractMatrix{Float64}, Float64}, - sys::AbstractQuantumSystem; - subspace=nothing, - integrator=exp -) - Ũ⃗_final = unitary_rollout(controls, Δt, sys; integrator=integrator)[:, end] - Ũ⃗_goal = operator_to_iso_vec(U_goal) - return unitary_fidelity(Ũ⃗_final, Ũ⃗_goal; subspace=subspace) -end -QuantumUtils.unitary_fidelity( +# ----------------------------------------------------------------------------- # +# Experimental rollouts +# ----------------------------------------------------------------------------- # + +Losses.unitary_fidelity( U_goal::EmbeddedOperator, controls::AbstractMatrix{Float64}, Δt::Union{AbstractVector{Float64}, AbstractMatrix{Float64}, Float64}, @@ -294,73 +427,103 @@ function lab_frame_unitary_rollout_trajectory( ) end -""" - sample_at(controls::AbstractMatrix{Float64}, t::Float64, ts::AbstractVector{Float64}; interp_method::Symbol=:linear) - -Sample the controls at time `t` using linear interpolation. - -# Keyword Arguments -- `interp_method::Symbol=:linear`: Interpolation method. Either `:linear` or `:constant`. -""" -function sample_at( - t::Float64, - controls::AbstractMatrix{Float64}, - ts::AbstractVector{Float64}; - interp_method::Symbol=:linear -) - @assert interp_method ∈ (:constant, :linear) "interp_method must be either :constant or :linear" - @assert t >= 0 "t must be non-negative" - @assert t <= ts[end] "t must be less than the duration of the controls" - if interp_method == :constant - return controls[:, searchsortedfirst(ts, t)] - else - i = searchsortedfirst(ts, t) - if i == 1 - return controls[:, 1] - else - t_prev = ts[i - 1] - t_next = ts[i] - α = (t - t_prev) / (t_next - t_prev) - return (1 - α) * controls[:, i - 1] + α * controls[:, i] - end - end -end - +# ============================================================================= # +@testitem "Test rollouts using fidelities" begin + using ExponentialAction + + sys = QuantumSystem(0 * GATES[:Z], [GATES[:X], GATES[:Y]]) + U_goal = GATES[:X] + embedded_U_goal = EmbeddedOperator(U_goal, sys) + T = 51 + Δt = 0.2 + ts = fill(Δt, T) + as = collect([π/(T-1)/Δt * sin.(π*(0:T-1)/(T-1)).^2 zeros(T)]') + + prob = UnitarySmoothPulseProblem( + sys, U_goal, T, Δt, a_guess=as, + ipopt_options=IpoptOptions(print_level=1), + piccolo_options=PiccoloOptions(verbose=false, free_time=false) + ) -function resample( - controls::AbstractMatrix{Float64}, - ts::AbstractVector{Float64}, - ts_new::AbstractVector{Float64}; - kwargs... -) - controls_new = stack([sample_at(t, controls, ts; kwargs...) for t ∈ ts_new]) - controls_new[:, end] = controls[:, end] - return controls_new + ψ = ComplexF64[1, 0] + ψ_goal = U_goal * ψ + ψ̃ = ket_to_iso(ψ) + ψ̃_goal = ket_to_iso(ψ_goal) + + # Default integrator + # State fidelity + @test fidelity(ψ, ψ_goal, as, ts, sys) ≈ 1 + @test iso_fidelity(ψ̃, ψ̃_goal, as, ts, sys) ≈ 1 + + # Unitary fidelity + @test unitary_fidelity(U_goal, as, ts, sys) ≈ 1 + @test unitary_fidelity(prob.trajectory, sys) ≈ 1 + @test unitary_fidelity(prob) ≈ 1 + @test unitary_fidelity(embedded_U_goal, as, ts, sys) ≈ 1 + + # Expv explicit + # State fidelity + @test fidelity(ψ, ψ_goal, as, ts, sys, integrator=expv) ≈ 1 + @test iso_fidelity(ψ̃, ψ̃_goal, as, ts, sys, integrator=expv) ≈ 1 + + # Unitary fidelity + @test unitary_fidelity(U_goal, as, ts, sys, integrator=expv) ≈ 1 + @test unitary_fidelity(prob.trajectory, sys, integrator=expv) ≈ 1 + @test unitary_fidelity(prob, integrator=expv) ≈ 1 + @test unitary_fidelity(embedded_U_goal, as, ts, sys, integrator=expv) ≈ 1 + + # Exp explicit + # State fidelity + @test fidelity(ψ, ψ_goal, as, ts, sys, integrator=exp) ≈ 1 + @test iso_fidelity(ψ̃, ψ̃_goal, as, ts, sys, integrator=exp) ≈ 1 + + # Unitary fidelity + @test unitary_fidelity(U_goal, as, ts, sys, integrator=exp) ≈ 1 + @test unitary_fidelity(prob.trajectory, sys, integrator=exp) ≈ 1 + @test unitary_fidelity(prob, integrator=exp) ≈ 1 + @test unitary_fidelity(embedded_U_goal, as, ts, sys, integrator=exp) ≈ 1 + + # Bad integrator + @test_throws ErrorException unitary_fidelity(U_goal, as, ts, sys, integrator=(a,b) -> 1) ≈ 1 end -function resample(controls, ts, Δt::Float64; kwargs...) - n_samples = Int(ts[end] / Δt) - ts_new = range(0, step=Δt, length=n_samples) - return resample(controls, ts, ts_new) -end +@testitem "Foward diff rollout" begin + using ForwardDiff + using ExponentialAction -function resample(controls, ts, N::Int; kwargs...) - Δt = ts[end] / N - ts_new = range(0, step=Δt, length=N) - return resample(controls, ts, ts_new; kwargs...) -end + sys = QuantumSystem(0 * GATES[:Z], [GATES[:X], GATES[:Y]]) + T = 51 + Δt = 0.2 + ts = fill(Δt, T) + as = collect([π/(T-1)/Δt * sin.(π*(0:T-1)/(T-1)).^2 zeros(T)]') -""" - resample(controls::AbstractMatrix{Float64}, ts::AbstractVector{Float64}, ts_new::AbstractVector{Float64}) - resample(controls::AbstractMatrix{Float64}, ts::AbstractVector{Float64}, Δt::Float64) - resample(controls::AbstractMatrix{Float64}, ts::AbstractVector{Float64}, N::Int) + # Control derivatives + ψ = ComplexF64[1, 0] + result1 = ForwardDiff.jacobian( + as -> rollout(ψ, as, ts, sys, integrator=expv)[:, end], as + ) + iso_ket_dim = length(ket_to_iso(ψ)) + @test size(result1) == (iso_ket_dim, T * length(sys.H_drives)) -Resample the controls at new time points. + result2 = ForwardDiff.jacobian( + as -> unitary_rollout(as, ts, sys, integrator=expv)[:, end], as + ) + iso_vec_dim = length(operator_to_iso_vec(sys.H_drift)) + @test size(result2) == (iso_vec_dim, T * length(sys.H_drives)) -# Keyword Arguments -- `interp_method::Symbol=:linear`: Interpolation method. Either `:linear` or `:constant`. -""" -function resample end + # Time derivatives + ψ = ComplexF64[1, 0] + result1 = ForwardDiff.jacobian( + ts -> rollout(ψ, as, ts, sys, integrator=expv)[:, end], ts + ) + iso_ket_dim = length(ket_to_iso(ψ)) + @test size(result1) == (iso_ket_dim, T) + result2 = ForwardDiff.jacobian( + ts -> unitary_rollout(as, ts, sys, integrator=expv)[:, end], ts + ) + iso_vec_dim = length(operator_to_iso_vec(sys.H_drift)) + @test size(result2) == (iso_vec_dim, T) +end end diff --git a/src/structure_utils.jl b/src/structure_utils.jl index 96ec9421..e7dae291 100644 --- a/src/structure_utils.jl +++ b/src/structure_utils.jl @@ -12,6 +12,8 @@ export dynamics_jacobian_structure export dynamics_hessian_of_lagrangian_structure export dynamics_structure +export sparsity_structure + export loss_hessian_structure using NamedTrajectories @@ -22,6 +24,21 @@ using Symbolics using ForwardDiff using Einsum +function sparsity_structure(S::SparseMatrixCSC; return_pairs=false, type::Type=Float64) + Is, Js = findnz(S) + if return_pairs + return zip(Is, Js) + else + return sparse(Is, Js, ones(type, nnz(S)), size(S)...) + end +end + +function sparsity_structure(A::AbstractMatrix; kwargs...) + return sparsity_structure(sparse(A); kwargs...) +end + + + function upper_half_vals(A::AbstractMatrix) n = size(A, 1) vals = similar(A, n * (n + 1) ÷ 2) @@ -203,7 +220,7 @@ function dynamics_structure( # getting symbolic variables z1 = collect(Symbolics.@variables(z[1:traj.dim])...) z2 = collect(Symbolics.@variables(z[1:traj.dim])...) - ∂f_structure = structure(sparse(∂f(z1, z2))) + ∂f_structure = structure(sparse(∂f(z1, z2, 1))) else if verbose println(" computing full jacobian block...") diff --git a/src/trajectory_initialization.jl b/src/trajectory_initialization.jl index 47015150..32f3c567 100644 --- a/src/trajectory_initialization.jl +++ b/src/trajectory_initialization.jl @@ -9,15 +9,23 @@ export convert_fixed_time export convert_free_time using NamedTrajectories -using LinearAlgebra + using Distributions +using ExponentialAction +using LinearAlgebra +using TestItemRunner -using ..QuantumUtils +using ..Isomorphisms using ..QuantumSystems using ..Rollouts using ..EmbeddedOperators using ..DirectSums + +# ----------------------------------------------------------------------------- # +# Initial states # +# ----------------------------------------------------------------------------- # + """ unitary_linear_interpolation( U_init::AbstractMatrix, @@ -43,7 +51,7 @@ function unitary_linear_interpolation( U_init::AbstractMatrix{<:Number}, U_goal::EmbeddedOperator, samples::Int -) +) return unitary_linear_interpolation(U_init, U_goal.operator, samples) end @@ -83,7 +91,7 @@ function unitary_geodesic( U_goal::EmbeddedOperator, samples::Int; kwargs... -) +) U1 = unembed(U_init, U_goal) U2 = unembed(U_goal) Ũ⃗ = unitary_geodesic(U1, U2, samples; kwargs...) @@ -103,7 +111,7 @@ function unitary_geodesic( end function unitary_geodesic( - U_goal::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + U_goal::OperatorType, samples::Int; kwargs... ) @@ -117,6 +125,7 @@ function unitary_geodesic( U_init::AbstractMatrix{<:Number}, U_goal::AbstractMatrix{<:Number}, timesteps::AbstractVector{<:Number}; + return_unitary_isos=true, return_generator=false ) """ @@ -134,11 +143,19 @@ function unitary_geodesic( H = im * log(U_goal * U_init') / T # -im prefactor is not included in H U_geo = [exp(-im * H * (t - t₀)) * U_init for t ∈ timesteps] - Ũ⃗_geo = stack(operator_to_iso_vec.(U_geo), dims=2) - if return_generator - return Ũ⃗_geo, H + if !return_unitary_isos + if return_generator + return U_geo, H + else + return U_geo + end else - return Ũ⃗_geo + Ũ⃗_geo = stack(operator_to_iso_vec.(U_geo), dims=2) + if return_generator + return Ũ⃗_geo, H + else + return Ũ⃗_geo + end end end @@ -152,7 +169,7 @@ const ScalarBound = Union{R, Tuple{R, R}} where R <: Real function initialize_unitaries( U_init::AbstractMatrix{<:Number}, - U_goal::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + U_goal::OperatorType, T::Int; geodesic=true ) @@ -164,6 +181,10 @@ function initialize_unitaries( return Ũ⃗ end +# ----------------------------------------------------------------------------- # +# Initial controls # +# ----------------------------------------------------------------------------- # + function initialize_controls( n_drives::Int, n_derivatives::Int, @@ -212,10 +233,22 @@ end initialize_controls(a::AbstractMatrix, Δt::Real, n_derivatives::Int) = initialize_controls(a, fill(Δt, size(a, 2)), n_derivatives) +# ----------------------------------------------------------------------------- # +# Trajectory initialization # +# ----------------------------------------------------------------------------- # + +""" + initialize_unitary_trajectory + + +Initialize a trajectory for a unitary control problem. The trajectory is initialized with +data that should be consistently the same type (in this case, Float64). + +""" function initialize_unitary_trajectory( - U_goal::Union{EmbeddedOperator, AbstractMatrix{<:Number}}, + U_goal::OperatorType, T::Int, - Δt::Real, + Δt::Union{Float64, AbstractVecOrMat{<:Float64}}, n_drives::Int, all_a_bounds::NamedTuple{anames, <:Tuple{Vararg{VectorBound}}} where anames; U_init::AbstractMatrix{<:Number}=Matrix{ComplexF64}(I(size(U_goal, 1))), @@ -227,12 +260,27 @@ function initialize_unitary_trajectory( drive_derivative_σ::Float64=0.1, a_guess::Union{AbstractMatrix{<:Float64}, Nothing}=nothing, system::Union{AbstractQuantumSystem, AbstractVector{<:AbstractQuantumSystem}, Nothing}=nothing, - rollout_integrator::Function=exp, + global_data::Union{NamedTuple, Nothing}=nothing, + rollout_integrator::Function=expv, Ũ⃗_keys::AbstractVector{<:Symbol}=[:Ũ⃗], a_keys::AbstractVector{<:Symbol}=[Symbol("d"^i * "a") for i in 0:n_derivatives] ) @assert length(a_keys) == n_derivatives + 1 "a_keys must have the same length as n_derivatives + 1" + if free_time + if Δt isa Real + Δt = fill(Δt, 1, T) + elseif Δt isa AbstractVector + Δt = reshape(Δt, 1, :) + else + @assert size(Δt) == (1, T) "Δt must be a Real, AbstractVector, or 1x$(T) AbstractMatrix" + end + timestep = :Δt + else + @assert Δt isa Real "Δt must be a Real if free_time is false" + timestep = Δt + end + Ũ⃗_init = operator_to_iso_vec(U_init) if U_goal isa EmbeddedOperator Ũ⃗_goal = operator_to_iso_vec(U_goal.operator) @@ -269,23 +317,35 @@ function initialize_unitary_trajectory( Ũ⃗_values = repeat([Ũ⃗], length(Ũ⃗_keys)) a_values = initialize_controls( n_drives, - n_derivatives, - T, - bounds[a_keys[1]], + n_derivatives, + T, + bounds[a_keys[1]], drive_derivative_σ ) else + @assert size(a_guess, 1) == n_drives "a_guess must have the same number of drives as n_drives" + @assert size(a_guess, 2) == T "a_guess must have the same number of timesteps as T" @assert !isnothing(system) "system must be provided if a_guess is provided" + + if Δt isa AbstractMatrix + ts = vec(Δt) + elseif Δt isa Float64 + ts = fill(Δt, T) + else + ts = Δt + end + if system isa AbstractVector @assert length(system) == length(Ũ⃗_keys) "systems must have the same length as Ũ⃗_keys" Ũ⃗_values = map(system) do sys - unitary_rollout(Ũ⃗_init, a_guess, Δt, sys; integrator=rollout_integrator) + unitary_rollout(Ũ⃗_init, a_guess, ts, sys; integrator=rollout_integrator) end else - Ũ⃗ = unitary_rollout(Ũ⃗_init, a_guess, Δt, system; integrator=rollout_integrator) + Ũ⃗ = unitary_rollout(Ũ⃗_init, a_guess, ts, system; integrator=rollout_integrator) Ũ⃗_values = repeat([Ũ⃗], length(Ũ⃗_keys)) end - a_values = initialize_controls(a_guess, Δt, n_derivatives) + Ũ⃗_values = Matrix{Float64}.(Ũ⃗_values) + a_values = initialize_controls(a_guess, ts, n_derivatives) end # Trajectory @@ -294,13 +354,11 @@ function initialize_unitary_trajectory( if free_time push!(keys, :Δt) - push!(values, fill(Δt, 1, T)) + push!(values, Δt) controls = (a_keys[end], :Δt) - timestep = :Δt bounds = merge(bounds, (Δt = Δt_bounds,)) else controls = (a_keys[end],) - timestep = Δt end return NamedTrajectory( @@ -310,7 +368,8 @@ function initialize_unitary_trajectory( bounds=bounds, initial=initial, final=final, - goal=goal + goal=goal, + global_data= isnothing(global_data) ? (;) : global_data ) end @@ -318,7 +377,7 @@ function initialize_quantum_state_trajectory( ψ̃_goals::AbstractVector{<:AbstractVector{<:Real}}, ψ̃_inits::AbstractVector{<:AbstractVector{<:Real}}, T::Int, - Δt::Real, + Δt::Union{Real, AbstractVector{<:Real}}, n_drives::Int, all_a_bounds::NamedTuple{anames, <:Tuple{Vararg{VectorBound}}} where anames; n_derivatives::Int=2, @@ -327,13 +386,24 @@ function initialize_quantum_state_trajectory( drive_derivative_σ::Float64=0.1, a_guess::Union{AbstractMatrix{<:Float64}, Nothing}=nothing, system::Union{AbstractQuantumSystem, AbstractVector{<:AbstractQuantumSystem}, Nothing}=nothing, - rollout_integrator::Function=exp, + global_data::Union{NamedTuple, Nothing}=nothing, + rollout_integrator::Function=exp, ψ̃_keys::AbstractVector{<:Symbol}=[Symbol("ψ̃$i") for i = 1:length(ψ̃_goals)], - a_keys::AbstractVector{<:Symbol}=[Symbol("d"^i * "a") for i in 0:n_derivatives] + a_keys::AbstractVector{<:Symbol}=[Symbol("d"^i * "a") for i = 0:n_derivatives] ) @assert length(ψ̃_inits) == length(ψ̃_goals) "ψ̃_inits and ψ̃_goals must have the same length" @assert length(ψ̃_keys) == length(ψ̃_goals) "ψ̃_keys and ψ̃_goals must have the same length" + if free_time + if Δt isa Real + Δt = fill(Δt, 1, T) + elseif Δt isa AbstractVector + Δt = reshape(Δt, 1, :) + else + @assert size(Δt) == (1, T) "Δt must be a Real, AbstractVector, or 1x$(T) AbstractMatrix" + end + end + # Constraints state_initial = (; (ψ̃_keys .=> ψ̃_inits)...) control_initial = (a = zeros(n_drives),) @@ -353,16 +423,16 @@ function initialize_quantum_state_trajectory( for (k, ψ̃_init, ψ̃_goal) in zip(ψ̃_keys, ψ̃_inits, ψ̃_goals) ]) a_values = initialize_controls( - n_drives, - n_derivatives, - T, - bounds[a_keys[1]], + n_drives, + n_derivatives, + T, + bounds[a_keys[1]], drive_derivative_σ ) else ψ̃_values = NamedTuple([ k => rollout(ψ̃_init, a_guess, Δt, system, integrator=rollout_integrator) - for (i, ψ̃_init) in zip(ψ̃_keys, ψ̃_inits) + for (k, ψ̃_init) in zip(ψ̃_keys, ψ̃_inits) ]) a_values = initialize_controls(a_guess, Δt, n_derivatives) end @@ -373,12 +443,13 @@ function initialize_quantum_state_trajectory( if free_time push!(keys, :Δt) - push!(values, fill(Δt, 1, T)) + push!(values, Δt) controls = (a_keys[end], :Δt) timestep = :Δt bounds = merge(bounds, (Δt = Δt_bounds,)) else controls = (a_keys[end],) + @assert Δt isa Real "Δt must be a Real if free_time is false" timestep = Δt end @@ -389,7 +460,8 @@ function initialize_quantum_state_trajectory( bounds=bounds, initial=initial, final=final, - goal=goal + goal=goal, + global_data=isnothing(global_data) ? (;) : global_data ) end @@ -417,7 +489,7 @@ function remove_component( end function convert_fixed_time( - traj::NamedTrajectory; + traj::NamedTrajectory; Δt_symb=:Δt, timestep = sum(get_timesteps(traj)) / traj.T ) @@ -435,7 +507,7 @@ end function convert_free_time( traj::NamedTrajectory, - Δt_bounds::Union{ScalarBound, BoundType}; + Δt_bounds::Union{ScalarBound, BoundType}; Δt_symb=:Δt, ) @assert Δt_symb ∉ traj.control_names "Problem must not be free time" @@ -455,4 +527,130 @@ function convert_free_time( ) end +# ============================================================================= # + +@testitem "Random drive initialization" begin + T = 10 + n_drives = 2 + n_derivates = 2 + drive_bounds = [1.0, 2.0] + drive_derivative_σ = 0.01 + + a, da, dda = TrajectoryInitialization.initialize_controls(n_drives, n_derivates, T, drive_bounds, drive_derivative_σ) + + @test size(a) == (n_drives, T) + @test size(da) == (n_drives, T) + @test size(dda) == (n_drives, T) + @test all([-drive_bounds[i] < minimum(a[i, :]) < drive_bounds[i] for i in 1:n_drives]) +end + +@testitem "Geodesic" begin + using LinearAlgebra + + ## Group 1: identity to X (π rotation) + + # Test π rotation + U_α = GATES[:I] + U_ω = GATES[:X] + Us, H = unitary_geodesic( + U_α, U_ω, range(0, 1, 4), return_generator=true + ) + + @test size(Us, 2) == 4 + @test Us[:, 1] ≈ operator_to_iso_vec(U_α) + @test Us[:, end] ≈ operator_to_iso_vec(U_ω) + @test H' - H ≈ zeros(2, 2) + @test norm(H) ≈ π + + # Test modified timesteps (10x) + Us10, H10 = unitary_geodesic( + U_α, U_ω, range(-5, 5, 4), return_generator=true + ) + + @test size(Us10, 2) == 4 + @test Us10[:, 1] ≈ operator_to_iso_vec(U_α) + @test Us10[:, end] ≈ operator_to_iso_vec(U_ω) + @test H10' - H10 ≈ zeros(2, 2) + @test norm(H10) ≈ π/10 + + # Test wrapped call + Us_wrap, H_wrap = unitary_geodesic(U_ω, 10, return_generator=true) + @test Us_wrap[:, 1] ≈ operator_to_iso_vec(GATES[:I]) + @test Us_wrap[:, end] ≈ operator_to_iso_vec(U_ω) + rotation = [exp(-im * H_wrap * t) for t ∈ range(0, 1, 10)] + Us_test = stack(operator_to_iso_vec.(rotation), dims=2) + @test isapprox(Us_wrap, Us_test) + + + ## Group 2: √X to X (π/2 rotation) + + # Test geodesic not at identity + U₀ = sqrt(GATES[:X]) + U₁ = GATES[:X] + Us, H = unitary_geodesic(U₀, U₁, 10, return_generator=true) + @test Us[:, 1] ≈ operator_to_iso_vec(U₀) + @test Us[:, end] ≈ operator_to_iso_vec(U_ω) + + rotation = [exp(-im * H * t) * U₀ for t ∈ range(0, 1, 10)] + Us_test = stack(operator_to_iso_vec.(rotation), dims=2) + @test isapprox(Us, Us_test) + Us_wrap = unitary_geodesic(U_ω, 4) + @test Us_wrap[:, 1] ≈ operator_to_iso_vec(GATES[:I]) + @test Us_wrap[:, end] ≈ operator_to_iso_vec(U_ω) + +end + +@testitem "Free and fixed time conversion" begin + using NamedTrajectories + include("../test/test_utils.jl") + + free_traj = named_trajectory_type_1(free_time=true) + fixed_traj = named_trajectory_type_1(free_time=false) + Δt_bounds = free_traj.bounds[:Δt] + + # Test free to fixed time + @test :Δt ∉ convert_fixed_time(free_traj).control_names + + # Test fixed to free time + @test :Δt ∈ convert_free_time(fixed_traj, Δt_bounds).control_names + + # Test inverses + @test convert_free_time(convert_fixed_time(free_traj), Δt_bounds) == free_traj + @test convert_fixed_time(convert_free_time(fixed_traj, Δt_bounds)) == fixed_traj +end + +@testitem "unitary trajectory initialization" begin + using NamedTrajectories + U_goal = GATES[:X] + T = 10 + Δt = 0.1 + n_drives = 2 + all_a_bounds = (a = [1.0, 1.0],) + + traj = initialize_unitary_trajectory( + U_goal, T, Δt, n_drives, all_a_bounds + ) + + @test traj isa NamedTrajectory +end + +@testitem "quantum state trajectory initialization" begin + using NamedTrajectories + + ψ̃_init = ket_to_iso([0.0, 1.0]) + ψ̃_goal = ket_to_iso([1.0, 0.0]) + + T = 10 + Δt = 0.1 + n_drives = 2 + all_a_bounds = (a = [1.0, 1.0],) + + traj = initialize_quantum_state_trajectory( + [ψ̃_goal], [ψ̃_init], T, Δt, n_drives, all_a_bounds + ) + + @test traj isa NamedTrajectory +end + + end diff --git a/test/direct_sums_test.jl b/test/direct_sums_test.jl deleted file mode 100644 index 0e2081b3..00000000 --- a/test/direct_sums_test.jl +++ /dev/null @@ -1,182 +0,0 @@ -""" -Tests: DirectSums submodule -""" - -@testitem "Apply suffix to trajectories" begin - using NamedTrajectories - include("../test/test_utils.jl") - - traj = named_trajectory_type_1(free_time=false) - suffix = "_new" - new_traj = add_suffix(traj, suffix) - - @test new_traj.state_names == add_suffix(traj.state_names, suffix) - @test new_traj.control_names == add_suffix(traj.control_names, suffix) - - same_traj = add_suffix(traj, "") - @test traj == same_traj -end - -@testitem "Merge trajectories" begin - using NamedTrajectories - include("../test/test_utils.jl") - - traj = named_trajectory_type_1(free_time=false) - - # apply suffix - pf_traj1 = add_suffix(traj, "_1") - pf_traj2 = add_suffix(traj, "_2") - - # merge - new_traj = direct_sum(pf_traj1, pf_traj2) - - @test issetequal(new_traj.state_names, vcat(pf_traj1.state_names..., pf_traj2.state_names...)) - @test issetequal(new_traj.control_names, vcat(pf_traj1.control_names..., pf_traj2.control_names...)) - - # merge2 - new_traj2 = direct_sum([pf_traj1, pf_traj2]) - - @test new_traj == new_traj2 -end - -@testitem "Merge free time trajectories" begin - using NamedTrajectories - include("../test/test_utils.jl") - - traj = named_trajectory_type_1(free_time=false) - - # apply suffix - pf_traj1 = add_suffix(traj, "_1") - pf_traj2 = add_suffix(traj, "_2") - pf_traj3 = add_suffix(traj, "_3") - state_names = vcat(pf_traj1.state_names..., pf_traj2.state_names..., pf_traj3.state_names...) - control_names = vcat(pf_traj1.control_names..., pf_traj2.control_names..., pf_traj3.control_names...) - - # merge (without reduce) - new_traj_1 = direct_sum(direct_sum(pf_traj1, pf_traj2), pf_traj3, free_time=true) - @test new_traj_1.timestep isa Symbol - @test issetequal(new_traj_1.state_names, state_names) - @test issetequal(setdiff(new_traj_1.control_names, control_names), [new_traj_1.timestep]) - - # merge (with reduce) - new_traj_2 = direct_sum([pf_traj1, pf_traj2, pf_traj3], free_time=true) - @test new_traj_2.timestep isa Symbol - @test issetequal(new_traj_2.state_names, state_names) - @test issetequal(setdiff(new_traj_2.control_names, control_names), [new_traj_2.timestep]) - - # check equality - for c in new_traj_1.control_names - @test new_traj_1[c] == new_traj_2[c] - end - for s in new_traj_1.state_names - @test new_traj_1[s] == new_traj_2[s] - end -end - -@testitem "Merge systems" begin - using NamedTrajectories - include("../test/test_utils.jl") - - H_drift = 0.01 * GATES[:Z] - H_drives = [GATES[:X], GATES[:Y]] - T = 50 - sys = QuantumSystem(H_drift, H_drives, params=Dict(:T=>T)) - - # apply suffix and sum - sys2 = direct_sum( - add_suffix(sys, "_1"), - add_suffix(sys, "_2") - ) - - @test length(sys2.H_drives) == 4 - @test sys2.params[:T_1] == T - @test sys2.params[:T_2] == T - - # add another system - sys = QuantumSystem(H_drift, H_drives, params=Dict(:T=>T, :S=>2T)) - sys3 = direct_sum(sys2, add_suffix(sys, "_3")) - @test length(sys3.H_drives) == 6 - @test sys3.params[:T_3] == T - @test sys3.params[:S_3] == 2T -end - -@testitem "Get suffix" begin - using NamedTrajectories - - sys = QuantumSystem(0.01 * GATES[:Z], [GATES[:X], GATES[:Y]]) - T = 50 - Δt = 0.2 - ip_ops = IpoptOptions(print_level=1) - pi_ops = PiccoloOptions(verbose=false, free_time=false) - prob1 = UnitarySmoothPulseProblem(sys, GATES[:X], T, Δt, piccolo_options=pi_ops, ipopt_options=ip_ops) - prob2 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, piccolo_options=pi_ops, ipopt_options=ip_ops) - - # Direct sum problem with suffix extraction - # Note: Turn off control reset - direct_sum_prob = UnitaryDirectSumProblem([prob1, prob2], 0.99, drive_reset_ratio=0.0, ipopt_options=ip_ops) - prob1_got = get_suffix(direct_sum_prob, "1") - @test prob1_got.trajectory == add_suffix(prob1.trajectory, "1") - - # Mutate the direct sum problem - update!(prob1_got.trajectory, :a1, ones(size(prob1_got.trajectory[:a1]))) - @test prob1_got.trajectory != add_suffix(prob1.trajectory, "1") - - # Remove suffix during extraction - prob1_got_without = get_suffix(direct_sum_prob, "1", remove=true) - @test prob1_got_without.trajectory == prob1.trajectory -end - -@testitem "Append to default integrators" begin - sys = QuantumSystem(0.01 * GATES[:Z], [GATES[:Y]]) - T = 50 - Δt = 0.2 - ip_ops = IpoptOptions(print_level=1) - pi_false_ops = PiccoloOptions(verbose=false, free_time=false) - pi_true_ops = PiccoloOptions(verbose=false, free_time=true) - prob1 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, piccolo_options=pi_false_ops, ipopt_options=ip_ops) - prob2 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, piccolo_options=pi_true_ops, ipopt_options=ip_ops) - - suffix = "_new" - # UnitaryPadeIntegrator - prob1_new = add_suffix(prob1.integrators, suffix) - @test prob1_new[1].unitary_symb == add_suffix(prob1.integrators[1].unitary_symb, suffix) - @test prob1_new[1].drive_symb == add_suffix(prob1.integrators[1].drive_symb, suffix) - - # DerivativeIntegrator - @test prob1_new[2].variable == add_suffix(prob1.integrators[2].variable, suffix) - - # UnitaryPadeIntegrator with free time - prob2_new = add_suffix(prob2.integrators, suffix) - @test prob2_new[1].unitary_symb == add_suffix(prob2.integrators[1].unitary_symb, suffix) - @test prob2_new[1].drive_symb == add_suffix(prob2.integrators[1].drive_symb, suffix) - - # DerivativeIntegrator - @test prob2_new[2].variable == add_suffix(prob2.integrators[2].variable, suffix) -end - -@testitem "Free time get suffix" begin - using NamedTrajectories - - sys = QuantumSystem(0.01 * GATES[:Z], [GATES[:Y]]) - T = 50 - Δt = 0.2 - ops = IpoptOptions(print_level=1) - pi_false_ops = PiccoloOptions(verbose=false, free_time=false) - pi_true_ops = PiccoloOptions(verbose=false, free_time=true) - suffix = "_new" - timestep_symbol = :Δt - - prob1 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, piccolo_options=pi_false_ops, ipopt_options=ops) - traj1 = direct_sum(prob1.trajectory, add_suffix(prob1.trajectory, suffix), free_time=true) - - # Direct sum (shared timestep name) - @test get_suffix(traj1, suffix).timestep == timestep_symbol - @test get_suffix(traj1, suffix, remove=true).timestep == timestep_symbol - - prob2 = UnitarySmoothPulseProblem(sys, GATES[:Y], T, Δt, ipopt_options=ops, piccolo_options=pi_true_ops) - traj2 = add_suffix(prob2.trajectory, suffix) - - # Trajectory (unique timestep name) - @test get_suffix(traj2, suffix).timestep == add_suffix(timestep_symbol, suffix) - @test get_suffix(traj2, suffix, remove=true).timestep == timestep_symbol -end \ No newline at end of file diff --git a/test/dynamics_test.jl b/test/dynamics_test.jl deleted file mode 100644 index e2ec61c4..00000000 --- a/test/dynamics_test.jl +++ /dev/null @@ -1,175 +0,0 @@ -# Testing dynamics derivatives - - # @testitem "Dynamics" begin - # using NamedTrajectories - - # # initializing test system - # T = 5 - # H_drift = GATES[:Z] - # H_drives = [GATES[:X], GATES[:Y]] - # n_drives = length(H_drives) - - # system = QuantumSystem(H_drift, H_drives) - - # end - - # @testset "State Dynamics" begin - - # P = FourthOrderPade(system) - - # Z = NamedTrajectory( - # ( - # ψ̃ = randn(4, T), - # u = randn(2, T), - # du = randn(2, T) - # ), - # controls=:du, - # timestep=0.1, - # goal=(ψ̃ = [1, 0, 0, 0],) - # ) - - # function f(zₜ, zₜ₊₁) - # ψ̃ₜ₊₁ = zₜ₊₁[Z.components.ψ̃] - # ψ̃ₜ = zₜ[Z.components.ψ̃] - # uₜ = zₜ[Z.components.u] - # uₜ₊₁ = zₜ₊₁[Z.components.u] - # duₜ = zₜ[Z.components.du] - # δψ̃ = P(ψ̃ₜ₊₁, ψ̃ₜ, uₜ, Z.timestep) - # δu = uₜ₊₁ - uₜ - duₜ * Z.timestep - # return vcat(δψ̃, δu) - # end - - # dynamics = QuantumDynamics(f, Z) - - # # test dynamics jacobian - # shape = (Z.dims.states * (Z.T - 1), Z.dim * Z.T) - # @test all(ForwardDiff.jacobian(dynamics.F, Z.datavec) .≈ - # dense(dynamics.∂F(Z.datavec), dynamics.∂F_structure, shape)) - - - # # test dynamics hessian of the lagrangian - # shape = (Z.dim * Z.T, Z.dim * Z.T) - # μ = rand(Z.dims.states * (Z.T - 1)) - # @test all(ForwardDiff.hessian(Z⃗ -> μ' * dynamics.F(Z⃗), Z.datavec) .≈ - # dense(dynamics.μ∂²F(Z.datavec, μ), dynamics.μ∂²F_structure, shape)) - # end - - # @testitem "Unitary dynamics" begin - # U_init = GATES[:I] - # U_goal = GATES[:X] - - # Ũ⃗_init = operator_to_iso_vec(U_init) - # Ũ⃗_goal = operator_to_iso_vec(U_goal) - - # dt = 0.1 - - # Z = NamedTrajectory( - # ( - # Ũ⃗ = unitary_geodesic(U_goal, T), - # a = randn(n_drives, T), - # da = randn(n_drives, T), - # Δt = fill(dt, 1, T), - # ), - # controls=(:da,), - # timestep=:Δt, - # goal=(Ũ⃗ = Ũ⃗_goal,) - # ) - # end - - # @testset "UnitaryPadeIntegrator integrator + DerivativeIntegrator on a & da" begin - - # P = UnitaryPadeIntegrator(system, :Ũ⃗, :a) - # D = DerivativeIntegrator(:a, :da, Z) - - # f = [P, D] - - # dynamics = QuantumDynamics(f, Z) - - # # test dynamics jacobian - # shape = (Z.dims.states * (Z.T - 1), Z.dim * Z.T) - - # # display(dynamics.∂F(Z.datavec)) - - # J_dynamics = dense(dynamics.∂F(Z.datavec), dynamics.∂F_structure, shape) - # # display(sparse(J_dynamics)) - # # display(Z.data) - # # println(Z.dim) - # #display(Z.datavec) - # J_forward_diff = ForwardDiff.jacobian(dynamics.F, Z.datavec) - # # display(J_dynamics) - # # display(sparse(J_forward_diff)) - # @test all(J_forward_diff .≈ J_dynamics) - # # show_diffs(J_forward_diff, J_dynamics) - - # # test dynamics hessian of the lagrangian - # # shape = (Z.dim * Z.T, Z.dim * Z.T) - - # # μ = ones(Z.dims.states * (Z.T - 1)) - - # # HoL_dynamics = dense(dynamics.μ∂²F(Z.datavec, μ), dynamics.μ∂²F_structure, shape) - - # # hessian_atol = 1e-15 - - # # HoL_forward_diff = ForwardDiff.hessian(Z⃗ -> dot(μ, dynamics.F(Z⃗)), Z.datavec) - # # @test all(isapprox.(HoL_forward_diff, HoL_dynamics; atol=hessian_atol)) - # # show_diffs(HoL_forward_diff, HoL_dynamics; atol=hessian_atol) - # end - - # @testset "UnitaryPadeIntegrator integrator w/ autodiff + DerivativeIntegrator on a & da" begin - - # P = UnitaryPadeIntegrator(system, :Ũ⃗, :a, :Δt; order=10, autodiff=true) - # D = DerivativeIntegrator(:a, :da, :Δt, n_drives) - - # f = [P, D] - - # dynamics = QuantumDynamics(f, Z) - - # # test dynamics jacobian - # shape = (Z.dims.states * (Z.T - 1), Z.dim * Z.T) - - # J_dynamics = dense(dynamics.∂F(Z.datavec), dynamics.∂F_structure, shape) - - # J_forward_diff = ForwardDiff.jacobian(dynamics.F, Z.datavec) - # @test all(J_forward_diff .≈ J_dynamics) - # show_diffs(J_forward_diff, J_dynamics) - - # # test dynamics hessian of the lagrangian - # shape = (Z.dim * Z.T, Z.dim * Z.T) - - # μ = ones(Z.dims.states * (Z.T - 1)) - - # HoL_dynamics = dense(dynamics.μ∂²F(Z.datavec, μ), dynamics.μ∂²F_structure, shape) - - # hessian_atol = 1e-15 - - # HoL_forward_diff = ForwardDiff.hessian(Z⃗ -> dot(μ, dynamics.F(Z⃗)), Z.datavec) - # @test all(isapprox.(HoL_forward_diff, HoL_dynamics; atol=hessian_atol)) - # show_diffs(HoL_forward_diff, HoL_dynamics; atol=hessian_atol) - # end - - - - - # @testset "unitary FourthOrderPade dynamics function" begin - # P = FourthOrderPade(system) - - # function f2(zₜ, zₜ₊₁) - # Ũ⃗ₜ₊₁ = zₜ₊₁[Z.components.Ũ⃗] - # Ũ⃗ₜ = zₜ[Z.components.Ũ⃗] - # aₜ = zₜ[Z.components.a] - # Δtₜ = zₜ[Z.components.Δt][1] - - # δŨ⃗ = P(Ũ⃗ₜ₊₁, Ũ⃗ₜ, aₜ, Δtₜ) - # return δŨ⃗ - # end - # dynamics_2 = QuantumDynamics(f2, Z) - - - - # # function f() - # # end - # end - - end - -end diff --git a/test/embedded_operators_test.jl b/test/embedded_operators_test.jl deleted file mode 100644 index 6beb1ef4..00000000 --- a/test/embedded_operators_test.jl +++ /dev/null @@ -1,117 +0,0 @@ -""" -Tests: EmbeddedOperators submodule -""" - -@testitem "Basis labels" begin - levels = [3, 3] - labels = ["11", "12", "13", "21", "22", "23", "31", "32", "33"] - @test EmbeddedOperators.basis_labels(levels, baseline=1) == labels - - labels = ["1", "2", "3"] - @test EmbeddedOperators.basis_labels(3, baseline=1) == labels - @test EmbeddedOperators.basis_labels([3], baseline=1) == labels - - labels = ["0", "1", "2"] - @test EmbeddedOperators.basis_labels(3, baseline=0) == labels - @test EmbeddedOperators.basis_labels([3], baseline=0) == labels - - levels = [2, 2] - labels = ["00", "01", "10", "11"] - @test EmbeddedOperators.basis_labels(levels, baseline=0) == labels -end - -@testitem "Subspace Indices" begin - @test get_subspace_indices([1, 2], 3) == [1, 2] - # 2 * 2 = 4 elements - @test get_subspace_indices([1:2, 1:2], [3, 3]) == [1, 2, 4, 5] - # 1 * 1 = 1 element - @test get_subspace_indices([[2], [2]], [3, 3]) == [5] - # 1 * 2 = 2 elements - @test get_subspace_indices([[2], 1:2], [3, 3]) == [4, 5] -end - -@testitem "Subspace ENR Indices" begin - # 00, 01, 02x, 10, 11x, 12x, 20x, 21x, 22x - @test get_subspace_enr_indices(1, [3, 3]) == [1, 2, 4] - # 00, 01, 02, 10, 11, 12x, 20, 21x, 22x - @test get_subspace_enr_indices(2, [3, 3]) == [1, 2, 3, 4, 5, 7] - # 00, 01, 02, 10, 11, 12, 20, 21, 22x - @test get_subspace_enr_indices(3, [3, 3]) == [1, 2, 3, 4, 5, 6, 7, 8] - # 00, 01, 02, 10, 11, 12, 20, 21, 22 - @test get_subspace_enr_indices(4, [3, 3]) == [1, 2, 3, 4, 5, 6, 7, 8, 9] -end - -@testitem "Subspace Leakage Indices" begin - # TODO: Implement tests -end - -@testitem "Embedded operator" begin - # Embed X - op = Matrix{ComplexF64}([0 1; 1 0]) - embedded_op = Matrix{ComplexF64}([0 1 0 0; 1 0 0 0; 0 0 0 0; 0 0 0 0]) - @test embed(op, 1:2, 4) == embedded_op - embedded_op_struct = EmbeddedOperator(op, 1:2, 4) - @test embedded_op_struct.operator == embedded_op - @test embedded_op_struct.subspace_indices == 1:2 - @test embedded_op_struct.subsystem_levels == [4] - - # Properties - @test size(embedded_op_struct) == size(embedded_op) - @test size(embedded_op_struct, 1) == size(embedded_op, 1) - - # X^2 = I - x2 = (embedded_op_struct * embedded_op_struct).operator - id = get_subspace_identity(embedded_op_struct) - @test x2 == id - - # Embed X twice - op2 = op ⊗ op - embedded_op2 = [ - 0 0 0 0 1 0 0 0 0; - 0 0 0 1 0 0 0 0 0; - 0 0 0 0 0 0 0 0 0; - 0 1 0 0 0 0 0 0 0; - 1 0 0 0 0 0 0 0 0; - 0 0 0 0 0 0 0 0 0; - 0 0 0 0 0 0 0 0 0; - 0 0 0 0 0 0 0 0 0; - 0 0 0 0 0 0 0 0 0 - ] - subspace_indices = get_subspace_indices([1:2, 1:2], [3, 3]) - @test embed(op2, subspace_indices, 9) == embedded_op2 - embedded_op2_struct = EmbeddedOperator(op2, subspace_indices, [3, 3]) - @test embedded_op2_struct.operator == embedded_op2 - @test embedded_op2_struct.subspace_indices == subspace_indices - @test embedded_op2_struct.subsystem_levels == [3, 3] -end - -@testitem "Embedded operator from system" begin - CZ = GATES[:CZ] - a = annihilate(3) - σ_x = a + a' - σ_y = -1im*(a - a') - system = QuantumSystem([σ_x ⊗ σ_x, σ_y ⊗ σ_y]) - - op_explicit_qubit = EmbeddedOperator( - CZ, - system, - subspace=get_subspace_indices([1:2, 1:2], [3, 3]) - ) - op_implicit_qubit = EmbeddedOperator(CZ, system) - # This does not work (implicit puts indicies in 1:4) - @test op_implicit_qubit.operator != op_explicit_qubit.operator - # But the ops are the same - @test unembed(op_explicit_qubit) == unembed(op_implicit_qubit) - @test unembed(op_implicit_qubit) == CZ -end - -@testitem "Embedded operator from composite system" begin - @test_skip nothing -end - -@testitem "Embedded operator kron" begin - Z = GATES[:Z] - Ẑ = EmbeddedOperator(Z, 1:2, [4]) - @test unembed(Ẑ ⊗ Ẑ) == Z ⊗ Z -end - diff --git a/test/losses_test.jl b/test/losses_test.jl deleted file mode 100644 index ae0bc731..00000000 --- a/test/losses_test.jl +++ /dev/null @@ -1,92 +0,0 @@ -""" -Tests: Losses submodule -""" - -@testitem "Isovec Unitary Fidelity" begin - using LinearAlgebra - - U_X = get_gate(:X) - U_Y = get_gate(:Y) - - function test_isovec_unitary_fidelity(U₁::AbstractMatrix, U₂::AbstractMatrix) - Ũ⃗₁ = operator_to_iso_vec(U₁) - Ũ⃗₂ = operator_to_iso_vec(U₂) - return Losses.isovec_unitary_fidelity(Ũ⃗₁, Ũ⃗₂) - end - - for U in [U_X, U_Y] - @test U'U ≈ I - end - - # Test gate fidelity - @test test_isovec_unitary_fidelity(U_X, U_X) ≈ 1 - @test test_isovec_unitary_fidelity(U_X, U_Y) ≈ 0 - - - # Test asymmetric fidelity - U_fn(λ, φ) = [1 -exp(im * λ); exp(im * φ) exp(im * (φ + λ))] / sqrt(2) - U_1 = U_fn(π/4, π/3) - U_2 = U_fn(1.5, .33) - - for U in [U_1, U_2] - @test U'U ≈ I - end - - @test test_isovec_unitary_fidelity(U_1, U_1) ≈ 1 - @test test_isovec_unitary_fidelity(U_2, U_2) ≈ 1 - @test test_isovec_unitary_fidelity(U_1, U_2) ≈ abs(tr(U_1'U_2)) / 2 - - - # Test random fidelity - U_H1 = haar_random(2) - U_H2 = haar_random(2) - - for U in [U_H1, U_H2] - @test U'U ≈ I - end - - @test test_isovec_unitary_fidelity(U_H1, U_H1) ≈ 1 - @test test_isovec_unitary_fidelity(U_H2, U_H2) ≈ 1 - @test test_isovec_unitary_fidelity(U_H1, U_X) ≈ abs(tr(U_H1'U_X)) / 2 - @test test_isovec_unitary_fidelity(U_H1, U_H2) ≈ abs(tr(U_H1'U_H2)) / 2 -end - - -@testitem "Isovec Unitary Fidelity Subspace" begin - using LinearAlgebra - - function test_isovec_unitary_fidelity(U₁::AbstractMatrix, U₂::AbstractMatrix, args...) - Ũ⃗₁ = operator_to_iso_vec(U₁) - Ũ⃗₂ = operator_to_iso_vec(U₂) - return Losses.isovec_unitary_fidelity(Ũ⃗₁, Ũ⃗₂, args...) - end - - # Test random fidelity - test_subspaces = [ - get_subspace_indices([1:2, 1:1], [2, 2]), - get_subspace_indices([1:2, 2:2], [2, 2]), - ] - - for ii in test_subspaces - U_H1 = kron(haar_random(2), haar_random(2)) - U_H1_sub = U_H1[ii, ii] - U_H2 = kron(haar_random(2), haar_random(2)) - U_H2_sub = U_H2[ii, ii] - - # subspace may not be unitary - for U in [U_H1, U_H2] - @test U'U ≈ I - end - - fid = test_isovec_unitary_fidelity(U_H1, U_H2, (ii, ii)) - fid_sub = test_isovec_unitary_fidelity(U_H1_sub, U_H2_sub) - @test fid ≈ fid_sub - end -end - - -@testitem "Isovec Unitary Fidelity Gradient" begin - - - -end \ No newline at end of file diff --git a/test/objectives_test.jl b/test/objectives_test.jl deleted file mode 100644 index 0d163618..00000000 --- a/test/objectives_test.jl +++ /dev/null @@ -1,150 +0,0 @@ -""" - Testing objective struct functionality -""" - -@testitem "Quantum State Objective" begin - using LinearAlgebra - using NamedTrajectories - using ForwardDiff - include("test_utils.jl") - - T = 10 - - Z = NamedTrajectory( - (ψ̃ = randn(4, T), u = randn(2, T)), - controls=:u, - timestep=0.1, - goal=(ψ̃ = [1., 0., 0., 0.],) - ) - - loss = :InfidelityLoss - Q = 100.0 - - J = QuantumObjective(:ψ̃, Z, loss, Q) - - L = Z⃗ -> J.L(Z⃗, Z) - ∇L = Z⃗ -> J.∇L(Z⃗, Z) - ∂²L = Z⃗ -> J.∂²L(Z⃗, Z) - ∂²L_structure = J.∂²L_structure(Z) - - # test objective function gradient - @test ForwardDiff.gradient(L, Z.datavec) ≈ ∇L(Z.datavec) - - # test objective function hessian - shape = (Z.dim * Z.T, Z.dim * Z.T) - @test ForwardDiff.hessian(L, Z.datavec) ≈ dense(∂²L(Z.datavec), ∂²L_structure, shape) -end - -@testitem "Quadratic Regularizer Objective" begin - using LinearAlgebra - using NamedTrajectories - using ForwardDiff - include("test_utils.jl") - - T = 10 - - Z = NamedTrajectory( - (ψ̃ = randn(4, T), u = randn(2, T)), - controls=:u, - timestep=0.1, - goal=(ψ̃ = [1.0, 0.0, 0.0, 0.0],) - ) - - - J = QuadraticRegularizer(:u, Z, [1., 1.]) - - L = Z⃗ -> J.L(Z⃗, Z) - ∇L = Z⃗ -> J.∇L(Z⃗, Z) - ∂²L = Z⃗ -> J.∂²L(Z⃗, Z) - ∂²L_structure = J.∂²L_structure(Z) - - # test objective function gradient - - @test all(ForwardDiff.gradient(L, Z.datavec) .≈ ∇L(Z.datavec)) - - # test objective function hessian - shape = (Z.dim * Z.T, Z.dim * Z.T) - @test all(isapprox( - ForwardDiff.hessian(L, Z.datavec), - dense(∂²L(Z.datavec), ∂²L_structure, shape); - atol=1e-7 - )) -end - -@testitem "Quadratic Smoothness Regularizer Objective" begin - using LinearAlgebra - using NamedTrajectories - using ForwardDiff - include("test_utils.jl") - - T = 10 - - Z = NamedTrajectory( - (ψ̃ = randn(4, T), u = randn(2, T)), - controls=:u, - timestep=0.1, - goal=(ψ̃ = [1.0, 0.0, 0.0, 0.0],) - ) - - - J = QuadraticSmoothnessRegularizer(:u, Z, [1., 1.]) - - L = Z⃗ -> J.L(Z⃗, Z) - ∇L = Z⃗ -> J.∇L(Z⃗, Z) - ∂²L = Z⃗ -> J.∂²L(Z⃗, Z) - ∂²L_structure = J.∂²L_structure(Z) - - # test objective function gradient - - @test all(ForwardDiff.gradient(L, Z.datavec) .≈ ∇L(Z.datavec)) - - # test objective function hessian - shape = (Z.dim * Z.T, Z.dim * Z.T) - @test all(isapprox( - ForwardDiff.hessian(L, Z.datavec), - dense(∂²L(Z.datavec), ∂²L_structure, shape); - atol=1e-7 - )) -end - -@testitem "Unitary Objective" begin - using LinearAlgebra - using NamedTrajectories - using ForwardDiff - include("test_utils.jl") - - T = 10 - - U_init = GATES[:I] - U_goal = GATES[:X] - - Ũ⃗_init = operator_to_iso_vec(U_init) - Ũ⃗_goal = operator_to_iso_vec(U_goal) - - Z = NamedTrajectory( - (Ũ⃗ = randn(length(Ũ⃗_init), T), u = randn(2, T)), - controls=:u, - timestep=0.1, - initial=(Ũ⃗ = Ũ⃗_init,), - goal=(Ũ⃗ = Ũ⃗_goal,) - ) - - loss = :UnitaryInfidelityLoss - Q = 100.0 - - J = QuantumObjective(:Ũ⃗, Z, loss, Q) - - L = Z⃗ -> J.L(Z⃗, Z) - ∇L = Z⃗ -> J.∇L(Z⃗, Z) - ∂²L = Z⃗ -> J.∂²L(Z⃗, Z) - ∂²L_structure = J.∂²L_structure(Z) - - # test objective function gradient - @test all(ForwardDiff.gradient(L, Z.datavec) ≈ ∇L(Z.datavec)) - - # test objective function hessian - shape = (Z.dim * Z.T, Z.dim * Z.T) - H = dense(∂²L(Z.datavec), ∂²L_structure, shape) - H_forwarddiff = ForwardDiff.hessian(L, Z.datavec) - @test all(H .≈ H_forwarddiff) -end diff --git a/test/problems_test.jl b/test/problems_test.jl deleted file mode 100644 index 35c9d199..00000000 --- a/test/problems_test.jl +++ /dev/null @@ -1,57 +0,0 @@ -""" - Testing problem features - - TODO: - - test problem creation - - test problem iterations - - test problem saving - - test problem loading -""" - -@testitem "System creation" begin - # initializing test system - T = 5 - H_drift = GATES[:Z] - H_drives = [GATES[:X], GATES[:Y]] - n_drives = length(H_drives) - - system = QuantumSystem(H_drift, H_drives) - - # test system creation - - -end - -@testitem "Additional Objective" begin - H_drift = GATES[:Z] - H_drives = [GATES[:X], GATES[:Y]] - U_goal = GATES[:H] - T = 50 - Δt = 0.2 - - prob_vanilla = UnitarySmoothPulseProblem( - H_drift, H_drives, U_goal, T, Δt, - ipopt_options=IpoptOptions(print_level=1), - piccolo_options=PiccoloOptions(verbose=false), - ) - - J_extra = QuadraticSmoothnessRegularizer(:dda, prob_vanilla.trajectory, 10.0) - - prob_additional = UnitarySmoothPulseProblem( - H_drift, H_drives, U_goal, T, Δt, - ipopt_options=IpoptOptions(print_level=1), - piccolo_options=PiccoloOptions(verbose=false), - additional_objective=J_extra, - ) - - J_prob_vanilla = Problems.get_objective(prob_vanilla) - - J_additional = Problems.get_objective(prob_additional) - - Z = prob_vanilla.trajectory - Z⃗ = prob_vanilla.trajectory.datavec - - @test J_prob_vanilla.L(Z⃗, Z) + J_extra.L(Z⃗, Z) ≈ J_additional.L(Z⃗, Z) - - -end diff --git a/test/quantum_system_utils_test.jl b/test/quantum_system_utils_test.jl deleted file mode 100644 index 650e5170..00000000 --- a/test/quantum_system_utils_test.jl +++ /dev/null @@ -1,127 +0,0 @@ -""" -Tests: QuantumSystemUtils submodule -""" - - -@testitem "Lie algebra basis" begin - H_ops = Dict( - "X" => GATES[:X], - "Y" => GATES[:Y], - "Z" => GATES[:Z] - ) - - # Check 1 qubit with complete basis - gen = map(A -> kron_from_dict(A, H_ops), ["X", "Y"]) - basis = operator_algebra(gen, return_layers=false, verbose=false) - @test length(basis) == size(first(gen), 1)^2-1 - - # Check 1 qubit with complete basis and layers - gen = map(A -> kron_from_dict(A, H_ops), ["X", "Y"]) - basis, layers = operator_algebra(gen, return_layers=true, verbose=false) - @test length(basis) == size(first(gen), 1)^2-1 - - # Check 1 qubit with subspace - gen = map(A -> kron_from_dict(A, H_ops), ["X"]) - basis = operator_algebra(gen, verbose=false) - @test length(basis) == 1 - - # Check 2 qubit with complete basis - gen = map( - AB -> kron_from_dict(AB, H_ops), - ["XX+YY","XI", "YI", "IY", "IX"] - ) - basis = operator_algebra(gen, verbose=false) - @test length(basis) == size(first(gen), 1)^2-1 - - # Check 2 qubit with linearly dependent basis - gen = map( - AB -> kron_from_dict(AB, H_ops), - ["XX+YY", "XI", "XI", "IY", "IX"] - ) - basis = operator_algebra(gen, verbose=false) - @test length(basis) == length(gen) - - # Check 2 qubit with pair of 1-qubit subspaces - gen = map( - AB -> kron_from_dict(AB, H_ops), - ["XI", "YI", "IY", "IX"] - ) - basis = operator_algebra(gen, verbose=false) - @test length(basis) == 2 * (2^2 - 1) -end - - -@testitem "Lie Algebra reachability" begin - using LinearAlgebra - - H_ops = Dict( - "X" => GATES[:X], - "Y" => GATES[:Y], - "Z" => GATES[:Z] - ) - - # Check 1 qubit with complete basis - gen = map(A -> kron_from_dict(A, H_ops), ["X", "Y"]) - target = H_ops["Z"] - @test is_reachable(target, gen, compute_basis=true, verbose=false) - - # System - sys = QuantumSystem([GATES[:X], GATES[:Y], GATES[:Z]]) - target = GATES[:Z] - @test is_reachable(target, sys) - - # System with drift - sys = QuantumSystem(GATES[:Z], [GATES[:X]]) - target = GATES[:Z] - @test is_reachable(target, sys) - - # Check 2 qubit with complete basis - XI = GATES[:X] ⊗ GATES[:I] - IX = GATES[:I] ⊗ GATES[:X] - YI = GATES[:Y] ⊗ GATES[:I] - IY = GATES[:I] ⊗ GATES[:Y] - XX = GATES[:X] ⊗ GATES[:X] - YY = GATES[:Y] ⊗ GATES[:Y] - ZI = GATES[:Z] ⊗ GATES[:I] - IZ = GATES[:I] ⊗ GATES[:Z] - ZZ = GATES[:Z] ⊗ GATES[:Z] - - complete_gen = [XX+YY, XI, YI, IX, IY] - incomplete_gen = [XI, ZZ] - r = [0, 1, 2, 3, 4] - r /= norm(r) - R2 = exp(-im * sum([θ * H for (H, θ) in zip(complete_gen, r)])) - CZ = [1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 -1] - CX = [1 0 0 0; 0 1 0 0; 0 0 0 1; 0 0 1 0] - - # Pass - @test is_reachable(R2, complete_gen) - @test is_reachable(CZ, complete_gen) - @test is_reachable(CX, complete_gen) - @test is_reachable(XI, complete_gen) - - # Mostly fail - @test !is_reachable(R2, incomplete_gen) - @test !is_reachable(CZ, incomplete_gen) - @test !is_reachable(CX, incomplete_gen) - @test is_reachable(XI, incomplete_gen) - - # QuantumSystems - complete_gen_sys = QuantumSystem(complete_gen) - incomplete_gen_sys = QuantumSystem(incomplete_gen) - # Pass - @test is_reachable(R2, complete_gen_sys) - @test is_reachable(CZ, complete_gen_sys) - @test is_reachable(CX, complete_gen_sys) - @test is_reachable(XI, complete_gen_sys) - - # Mostly fail - @test !is_reachable(R2, incomplete_gen_sys) - @test !is_reachable(CZ, incomplete_gen_sys) - @test !is_reachable(CX, incomplete_gen_sys) - @test is_reachable(XI, incomplete_gen_sys) -end - -@testitem "Lie Algebra subspace reachability" begin - # TODO: implement tests -end diff --git a/test/quantum_systems_test.jl b/test/quantum_systems_test.jl deleted file mode 100644 index 81540c6b..00000000 --- a/test/quantum_systems_test.jl +++ /dev/null @@ -1,7 +0,0 @@ -""" -Tests: QuantumSystems submodule -""" - -@testset "Quantum Systems" begin - -end diff --git a/test/quantum_utils_test.jl b/test/quantum_utils_test.jl deleted file mode 100644 index b0981661..00000000 --- a/test/quantum_utils_test.jl +++ /dev/null @@ -1,107 +0,0 @@ -""" -Tests: QuantumUtils submodule -""" - -@testitem "GATES" begin - using LinearAlgebra - @test get_gate(:X) * get_gate(:X) == get_gate(:I) - @test get_gate(:Y) * get_gate(:Y) == get_gate(:I) - @test im*get_gate(:Y)*get_gate(:X) == get_gate(:Z) # Z = iYX - @test -im*get_gate(:X)*get_gate(:Y) == get_gate(:Z) # Z = -iXY - @test -im*get_gate(:Z)*get_gate(:X) == get_gate(:Y) # Y = -iZX - @test im*get_gate(:X)*get_gate(:Z) == get_gate(:Y) # Y = iXZ - @test im*get_gate(:Z)*get_gate(:Y) == get_gate(:X) # X = iZY - @test -im*get_gate(:Y)*get_gate(:Z) == get_gate(:X) # X = -iYZ - - @test isapprox(get_gate(:H)*get_gate(:X)*get_gate(:H)', get_gate(:Z), atol=1e-2) == true # H*X*H† = Z - @test isapprox(get_gate(:H)*get_gate(:Z)*get_gate(:H)', get_gate(:X), atol=1e-2) == true # H*Z*H† = X - @test isapprox(get_gate(:H)*get_gate(:Y)*get_gate(:H)', -get_gate(:Y), atol=1e-2) == true # H*Y*H† = -Y - - @test isapprox(get_gate(:H)*get_gate(:H), get_gate(:I), atol=1e-2) == true # H² = I - @test get_gate(:CX)*get_gate(:CX) == I(4) # CNOT² = I - @test get_gate(:CZ)*get_gate(:CZ) == I(4) # CZ² = I - @test GATES[:X] ^ 2 == GATES[:I] # X² = I - @test GATES[:Y] ^ 2 == GATES[:I] # Y² = I - @test isapprox(GATES[:Z] ^ 2, GATES[:I], atol=1e-2) == true # Z² = I - @test Int.(round.(real.(GATES[:X]*GATES[:Y] + GATES[:Y]*GATES[:X]))) == zeros(2, 2) - @test Int.(round.(real.(GATES[:Y]*GATES[:Z] + GATES[:Z]*GATES[:Y]))) == zeros(2, 2) - @test Int.(round.(real.(GATES[:Z]*GATES[:X] + GATES[:X]*GATES[:Z]))) == zeros(2, 2) -end - -@testitem "⊗" begin - using LinearAlgebra - @test Int.(real.(GATES[:I] ⊗ GATES[:I])) == I(4) - @test (GATES[:X] ⊗ GATES[:Y]) ⊗ GATES[:Z] == GATES[:X] ⊗ (GATES[:Y] ⊗ GATES[:Z]) # Associativity - @test GATES[:X] ⊗ (GATES[:Y] + GATES[:Z]) == GATES[:X] ⊗ GATES[:Y] + GATES[:X] ⊗ GATES[:Z] # Distributivity -end - -@testitem "Test apply function with Pauli gates" begin - using LinearAlgebra - ψ₀ = [1.0 + 0.0im, 0.0 + 0.0im] - ψ₁ = [0.0 + 0.0im, 1.0 + 0.0im] - @test apply(:X, ψ₀) == [0.0 + 0.0im, 1.0 + 0.0im] - @test apply(:X, ψ₁) == [1.0 + 0.0im, 0.0 + 0.0im] - @test apply(:Y, ψ₀) == GATES[:Y] * ψ₀ - @test apply(:Y, ψ₁) == GATES[:Y] * ψ₁ - @test apply(:H, apply(:H, [1, 0])) == [1, 0] # H * H = I - @test isapprox(apply(:H, apply(:H, apply(:H, [1, 0]))), [0.7071, 0.7071], atol=1e-1) == true - @test apply(:CX, [1, 0, 0, 0]) == [1, 0, 0, 0] # CNOT |00⟩ = |00⟩ - @test apply(:CX, [0, 1, 0, 0]) == [0, 1, 0, 0] # CNOT |01⟩ = |01⟩ - @test apply(:CX, [0, 0, 1, 0]) == [0, 0, 0, 1] # CNOT |10⟩ = |11⟩ - @test apply(:CX, [0, 0, 0, 1]) == [0, 0, 1, 0] # CNOT |11⟩ = |10⟩ - @test apply(:CZ, [1, 0, 0, 0]) == [1, 0, 0, 0] # CZ |00⟩ = |00⟩ - @test apply(:CZ, [0, 1, 0, 0]) == [0, 1, 0, 0] # CZ |01⟩ = |01⟩ - @test apply(:CZ, [0, 0, 1, 0]) == [0, 0, 1, 0] # CZ |10⟩ = |10⟩ - @test apply(:CZ, [0, 0, 0, 1]) == [0, 0, 0, -1] # CZ |11⟩ = -|11⟩ -end - -@testitem "Test qubit_system_state function" begin - using LinearAlgebra - @test qubit_system_state("0") == [1, 0] - @test qubit_system_state("1") == [0, 1] - @test qubit_system_state("00") == [1, 0, 0, 0] - @test qubit_system_state("01") == [0, 1, 0, 0] - @test qubit_system_state("10") == [0, 0, 1, 0] - @test qubit_system_state("11") == [0, 0, 0, 1] -end - -@testitem "Test lift function" begin - using LinearAlgebra - U1 = [1 0; 0 1] - @test size(lift(U1, 1, 2)) == (4, 4) - @test size(lift(U1, 2, 2)) == (4, 4) - @test size(lift(U1, 1, 3)) == (8, 8) -end - -@testitem "Test isomorphism utilities" begin - using LinearAlgebra - iso_vec = [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0] - @test vec⁻¹([1.0, 2.0, 3.0, 4.0]) == [1.0 3.0; 2.0 4.0] - @test ket_to_iso([1.0, 2.0]) == [1.0, 2.0, 0.0, 0.0] - @test iso_to_ket([1.0, 2.0, 0.0, 0.0]) == [1.0, 2.0] - @test iso_vec_to_operator(iso_vec) == [1.0 0.0; 0.0 1.0] - @test iso_vec_to_iso_operator(iso_vec) == [1.0 0.0 -0.0 -0.0; 0.0 1.0 -0.0 -0.0; 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 1.0] - @test operator_to_iso_vec(Complex[1.0 0.0; 0.0 1.0]) == iso_vec - @test iso_operator_to_iso_vec(iso_vec_to_iso_operator(iso_vec)) == iso_vec -end - -@testitem "quantum harmonic oscillator operators" begin - using LinearAlgebra - const tol = 1e-10 - levels = 2 - expected₂ = [0.0+0.0im 1.0+0.0im; - 0.0+0.0im 0.0+0.0im] - @test annihilate(levels) == expected₂ - levels = 3 - expected₃ = [0.0+0.0im 1.0+0.0im 0.0+0.0im; - 0.0+0.0im 0.0+0.0im 1.41421+0.0im; - 0.0+0.0im 0.0+0.0im 0.0+0.0im] - @test isapprox(expected₃, annihilate(3), atol=1e-2) == true - @test annihilate(2) == create(2)' - @test annihilate(3) == create(3)' - @test annihilate(4) == create(4)' - @test number(3) == create(3)* annihilate(3) - @test number(4) == create(4)* annihilate(4) - @test quad(3) == number(3) * (number(3) - I(3)) - @test quad(4) == number(4) * (number(4) - I(4)) -end diff --git a/test/rollouts_test.jl b/test/rollouts_test.jl deleted file mode 100644 index 053685d6..00000000 --- a/test/rollouts_test.jl +++ /dev/null @@ -1,7 +0,0 @@ -""" - Testing rollouts - - TODO: Add tests for rollouts -""" - - diff --git a/test/scripts/integrator_test_1qubit.jl b/test/scripts/integrator_test_1qubit.jl index fc2d9b6e..04032cfc 100644 --- a/test/scripts/integrator_test_1qubit.jl +++ b/test/scripts/integrator_test_1qubit.jl @@ -41,11 +41,11 @@ f = [P, D] dynamics = QuantumDynamics(f, Z) # test dynamics jacobian -shape = (Z.dims.states * (Z.T - 1), Z.dim * Z.T) +shape = (Z.dims.states * (Z.T - 1), Z.dim * Z.T + Z.global_dim) @btime dyn_res = dynamics.F(Z.datavec) @btime J_dynamics, J_struc = dynamics.∂F(Z.datavec), dynamics.∂F_structure -shape = (Z.dim * Z.T, Z.dim * Z.T) +shape = (Z.dim * Z.T + Z.global_dim, Z.dim * Z.T + Z.global_dim) μ = ones(Z.dims.states * (Z.T - 1)) diff --git a/test/scripts/integrator_test_script.jl b/test/scripts/integrator_test_script.jl index 9e656cfd..32896a4e 100644 --- a/test/scripts/integrator_test_script.jl +++ b/test/scripts/integrator_test_script.jl @@ -41,10 +41,10 @@ f = [P, D] dynamics = QuantumDynamics(f, Z) # test dynamics jacobian -shape = (Z.dims.states * (Z.T - 1), Z.dim * Z.T) +shape = (Z.dims.states * (Z.T - 1), Z.dim * Z.T + Z.global_dim) @btime dyn_res = dynamics.F(Z.datavec) @btime J_dynamics, J_struc = dynamics.∂F(Z.datavec), dynamics.∂F_structure -shape = (Z.dim * Z.T, Z.dim * Z.T) +shape = (Z.dim * Z.T + Z.global_dim, Z.dim * Z.T + Z.global_dim) μ = ones(Z.dims.states * (Z.T - 1)) diff --git a/test/trajectory_initialization_test.jl b/test/trajectory_initialization_test.jl deleted file mode 100644 index de3a0771..00000000 --- a/test/trajectory_initialization_test.jl +++ /dev/null @@ -1,93 +0,0 @@ -""" - Testing trajectory initialization -""" - -@testitem "Random drive initialization" begin - T = 10 - n_drives = 2 - n_derivates = 2 - drive_bounds = [1.0, 2.0] - drive_derivative_σ = 0.01 - - a, da, dda = TrajectoryInitialization.initialize_controls(n_drives, n_derivates, T, drive_bounds, drive_derivative_σ) - - @test size(a) == (n_drives, T) - @test size(da) == (n_drives, T) - @test size(dda) == (n_drives, T) - @test all([-drive_bounds[i] < minimum(a[i, :]) < drive_bounds[i] for i in 1:n_drives]) -end - -@testitem "Geodesic" begin - using LinearAlgebra - - ## Group 1: identity to X (π rotation) - - # Test π rotation - U_α = GATES[:I] - U_ω = GATES[:X] - Us, H = unitary_geodesic( - U_α, U_ω, range(0, 1, 4), return_generator=true - ) - - @test size(Us, 2) == 4 - @test Us[:, 1] ≈ operator_to_iso_vec(U_α) - @test Us[:, end] ≈ operator_to_iso_vec(U_ω) - @test H' - H ≈ zeros(2, 2) - @test norm(H) ≈ π - - # Test modified timesteps (10x) - Us10, H10 = unitary_geodesic( - U_α, U_ω, range(-5, 5, 4), return_generator=true - ) - - @test size(Us10, 2) == 4 - @test Us10[:, 1] ≈ operator_to_iso_vec(U_α) - @test Us10[:, end] ≈ operator_to_iso_vec(U_ω) - @test H10' - H10 ≈ zeros(2, 2) - @test norm(H10) ≈ π/10 - - # Test wrapped call - Us_wrap, H_wrap = unitary_geodesic(U_ω, 10, return_generator=true) - @test Us_wrap[:, 1] ≈ operator_to_iso_vec(GATES[:I]) - @test Us_wrap[:, end] ≈ operator_to_iso_vec(U_ω) - rotation = [exp(-im * H_wrap * t) for t ∈ range(0, 1, 10)] - Us_test = stack(operator_to_iso_vec.(rotation), dims=2) - @test isapprox(Us_wrap, Us_test) - - - ## Group 2: √X to X (π/2 rotation) - - # Test geodesic not at identity - U₀ = sqrt(GATES[:X]) - U₁ = GATES[:X] - Us, H = unitary_geodesic(U₀, U₁, 10, return_generator=true) - @test Us[:, 1] ≈ operator_to_iso_vec(U₀) - @test Us[:, end] ≈ operator_to_iso_vec(U_ω) - - rotation = [exp(-im * H * t) * U₀ for t ∈ range(0, 1, 10)] - Us_test = stack(operator_to_iso_vec.(rotation), dims=2) - @test isapprox(Us, Us_test) - Us_wrap = unitary_geodesic(U_ω, 4) - @test Us_wrap[:, 1] ≈ operator_to_iso_vec(GATES[:I]) - @test Us_wrap[:, end] ≈ operator_to_iso_vec(U_ω) - -end - -@testitem "Free and fixed time conversion" begin - using NamedTrajectories - include("test_utils.jl") - - free_traj = named_trajectory_type_1(free_time=true) - fixed_traj = named_trajectory_type_1(free_time=false) - Δt_bounds = free_traj.bounds[:Δt] - - # Test free to fixed time - @test :Δt ∉ convert_fixed_time(free_traj).control_names - - # Test fixed to free time - @test :Δt ∈ convert_free_time(fixed_traj, Δt_bounds).control_names - - # Test inverses - @test convert_free_time(convert_fixed_time(free_traj), Δt_bounds) == free_traj - @test convert_fixed_time(convert_free_time(fixed_traj, Δt_bounds)) == fixed_traj -end