From e62314dcf867b8bde5fce2cb3ed6538218fd3b33 Mon Sep 17 00:00:00 2001 From: Michael Goerz Date: Tue, 16 Jan 2024 21:11:21 -0500 Subject: [PATCH] Link to external examples --- .github/workflows/CI.yml | 9 +- LICENSE | 2 +- docs/data/.gitignore | 1 - docs/generate.jl | 36 -- docs/make.jl | 51 +- docs/src/api.md | 1 + docs/src/examples.md | 7 + docs/src/examples/.gitignore | 4 - docs/src/examples/index.md | 14 - docs/src/externals.md | 39 ++ docs/src/index.md | 38 +- docs/src/inventories/TimerOutputs.toml | 84 ++++ docs/src/refs.bib | 90 ++++ examples/perfect_entanglers.jl | 438 ------------------ .../positive_parametrization_comparison.jl | 120 ----- .../symmetric_parametrization_comparison.jl | 129 ------ examples/rho_3states.jl | 305 ------------ examples/simple_state_to_state.jl | 230 --------- examples/state_to_state_parametrizations.jl | 345 -------------- test/clean.jl | 11 - test/data/.gitignore | 1 - test/download_dumps.jl | 38 -- test/examples/.gitignore | 1 - test/generate_example_tests.jl | 22 - test/init.jl | 9 +- test/runtests.jl | 27 +- 26 files changed, 261 insertions(+), 1791 deletions(-) delete mode 100644 docs/data/.gitignore delete mode 100644 docs/generate.jl create mode 100644 docs/src/examples.md delete mode 100644 docs/src/examples/.gitignore delete mode 100644 docs/src/examples/index.md create mode 100644 docs/src/externals.md create mode 100644 docs/src/inventories/TimerOutputs.toml delete mode 100644 examples/perfect_entanglers.jl delete mode 100644 examples/plots/positive_parametrization_comparison.jl delete mode 100644 examples/plots/symmetric_parametrization_comparison.jl delete mode 100644 examples/rho_3states.jl delete mode 100644 examples/simple_state_to_state.jl delete mode 100644 examples/state_to_state_parametrizations.jl delete mode 100644 test/data/.gitignore delete mode 100644 test/download_dumps.jl delete mode 100644 test/examples/.gitignore delete mode 100644 test/generate_example_tests.jl diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f23c511..b17d7e1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -25,12 +25,12 @@ jobs: version: '1' arch: x64 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: julia-actions/cache@v1 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - uses: julia-actions/cache@v1 - run: | # Instantiate Pkg wget https://raw.githubusercontent.com/JuliaQuantumControl/JuliaQuantumControl/master/scripts/installorg.jl @@ -49,7 +49,8 @@ jobs: name: Documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: julia-actions/cache@v1 - uses: julia-actions/setup-julia@v1 with: version: '1' @@ -90,7 +91,7 @@ jobs: name: Codestyle runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 with: version: '1' diff --git a/LICENSE b/LICENSE index 18497fa..1ae7510 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Michael Goerz and contributors +Copyright (c) 2024 Michael Goerz and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/data/.gitignore b/docs/data/.gitignore deleted file mode 100644 index d390084..0000000 --- a/docs/data/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.jld2 diff --git a/docs/generate.jl b/docs/generate.jl deleted file mode 100644 index 8e8897b..0000000 --- a/docs/generate.jl +++ /dev/null @@ -1,36 +0,0 @@ -# generate examples -import Literate -using Base.Filesystem: cp, basename - -println("Start generating Literate.jl examples") - -EXAMPLEDIR = joinpath(@__DIR__, "..", "examples") -GENERATEDDIR = joinpath(@__DIR__, "src", "examples") -mkpath(GENERATEDDIR) -for name in readdir(EXAMPLEDIR) - path = abspath(joinpath(EXAMPLEDIR, name)) - if endswith(path, ".jl") - example = path - script = Literate.script(example, GENERATEDDIR) - code = strip(read(script, String)) - mdpost(str) = replace(str, "@__CODE__" => code) - Literate.markdown(example, GENERATEDDIR, postprocess=mdpost) - Literate.notebook(example, GENERATEDDIR, execute=true) - elseif any(endswith.(path, [".png", ".jpg", ".gif"])) - cp(path, joinpath(GENERATEDDIR, name); force=true) - elseif isdir(path) - folder = path - target = joinpath(GENERATEDDIR, basename(folder)) - cp(folder, target, force=true) - else - @warn "ignoring $name" - end -end - -# remove any .vtu files in the generated dir (should not be deployed) -cd(GENERATEDDIR) do - foreach(file -> endswith(file, ".vtu") && rm(file), readdir()) - foreach(file -> endswith(file, ".pvd") && rm(file), readdir()) -end - -println("Finished generating Literate.jl examples") diff --git a/docs/make.jl b/docs/make.jl index e0dc827..0f94055 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,4 +1,5 @@ using QuantumControlBase +using QuantumPropagators using Krotov using Documenter using DocumenterCitations @@ -9,12 +10,6 @@ using Plots gr() ENV["GKSwstype"] = "100" -include(joinpath("..", "test", "download_dumps.jl")) - -# Generate examples -include("generate.jl") - -DocMeta.setdocmeta!(Krotov, :DocTestSetup, :(using Krotov); recursive=true) PROJECT_TOML = Pkg.TOML.parsefile(joinpath(@__DIR__, "..", "Project.toml")) VERSION = PROJECT_TOML["version"] @@ -22,12 +17,37 @@ NAME = PROJECT_TOML["name"] AUTHORS = join(PROJECT_TOML["authors"], ", ") * " and contributors" GITHUB = "https://github.com/JuliaQuantumControl/Krotov.jl" +DEV_OR_STABLE = "stable/" +if endswith(VERSION, "dev") + DEV_OR_STABLE = "dev/" +end + +links = InterLinks( + "TimerOutputs" => ( + "https://github.com/KristofferC/TimerOutputs.jl", + joinpath(@__DIR__, "src", "inventories", "TimerOutputs.toml") + ), + "QuantumPropagators" => "https://juliaquantumcontrol.github.io/QuantumPropagators.jl/$DEV_OR_STABLE", + "QuantumControl" => "https://juliaquantumcontrol.github.io/QuantumControl.jl/$DEV_OR_STABLE", + "GRAPE" => "https://juliaquantumcontrol.github.io/GRAPE.jl/$DEV_OR_STABLE", + "Examples" => "https://juliaquantumcontrol.github.io/QuantumControlExamples.jl/$DEV_OR_STABLE", +) + println("Starting makedocs") bib = CitationBibliography(joinpath(@__DIR__, "src", "refs.bib"); style=:numeric) +PAGES = [ + "Home" => "index.md", + "Overview" => "overview.md", + "Examples" => "examples.md", + "API" => "api.md", + "References" => "references.md", + hide("externals.md"), +] + makedocs(; - plugins=[bib], + plugins=[bib, links], modules=[Krotov], authors=AUTHORS, sitename="Krotov.jl", @@ -43,27 +63,14 @@ makedocs(; "https://juliaquantumcontrol.github.io/QuantumControl.jl/dev/assets/topbar/topbar.js" ), ], + size_threshold_ignore=["externals.md"], mathengine=KaTeX(), footer="[$NAME.jl]($GITHUB) v$VERSION docs powered by [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl).", ), - pages=[ - "Home" => "index.md", - "Overview" => "overview.md", - "Examples" => [ - "List of Examples" => "examples/index.md", - "Example 1 (TLS)" => "examples/simple_state_to_state.md", - "Example 2 (Diss. Gate)" => "examples/rho_3states.md", - "Example 3 (Parametrization)" => "examples/state_to_state_parametrizations.md", - "Example 4 (PE)" => "examples/perfect_entanglers.md", - ], - "API" => "api.md", - "References" => "references.md", - ], + pages=PAGES, warnonly=true, ) println("Finished makedocs") -rm(joinpath(@__DIR__, "build", "examples", ".gitignore")) - deploydocs(; repo="github.com/JuliaQuantumControl/Krotov.jl", devbranch="master") diff --git a/docs/src/api.md b/docs/src/api.md index 16040e7..31e1fb0 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -1,6 +1,7 @@ # API ```@index +Pages = ["api.md"] ``` ```@autodocs diff --git a/docs/src/examples.md b/docs/src/examples.md new file mode 100644 index 0000000..55cbb36 --- /dev/null +++ b/docs/src/examples.md @@ -0,0 +1,7 @@ +# Examples + +The following examples illustrate the use of Krotov's method: + +* Example: [Entangling quantum gates for coupled transmon qubits](@extref Examples). Written for [GRAPE](@extref GRAPE :doc:`index`), but could use Krotov's method interchangeably. +* Example: [Optimization of a Dissipative Quantum Gate](@extref Examples) +* Tutorial: [Pulse Parametrization for Krotov's Method](@extref Examples) diff --git a/docs/src/examples/.gitignore b/docs/src/examples/.gitignore deleted file mode 100644 index 3b28521..0000000 --- a/docs/src/examples/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.md -*.jl -*.ipynb -!index.md diff --git a/docs/src/examples/index.md b/docs/src/examples/index.md deleted file mode 100644 index e7f5a13..0000000 --- a/docs/src/examples/index.md +++ /dev/null @@ -1,14 +0,0 @@ -# Examples - -```@contents -Pages = [ - "simple_state_to_state.md", - "state_to_state_rwa.md", - "rho_3states.md", - "state_to_state_parametrizations.md", - "perfect_entanglers.md", -] -Depth = 1 -``` - -See also the [general examples](https://juliaquantumcontrol.github.io/QuantumControl.jl/stable/examples/) of the [QuantumControl](https://juliaquantumcontrol.github.io/QuantumControl.jl/stable/) package. diff --git a/docs/src/externals.md b/docs/src/externals.md new file mode 100644 index 0000000..cd0ffc0 --- /dev/null +++ b/docs/src/externals.md @@ -0,0 +1,39 @@ +```@autodocs +Modules = [QuantumControlBase] +``` + +```@autodocs +Modules = [QuantumPropagators] +``` + +```@autodocs +Modules = [QuantumPropagators.Generators] +``` + +```@autodocs +Modules = [QuantumPropagators.Arnoldi] +``` + +```@autodocs +Modules = [QuantumPropagators.Interfaces] +``` + +```@autodocs +Modules = [QuantumPropagators.Controls] +``` + +```@autodocs +Modules = [QuantumPropagators.Storage] +``` + +```@autodocs +Modules = [QuantumPropagators.SpectralRange] +``` + +```@autodocs +Modules = [QuantumPropagators.Newton] +``` + +```@autodocs +Modules = [QuantumPropagators.Cheby] +``` diff --git a/docs/src/index.md b/docs/src/index.md index 3471e79..da6090a 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -17,7 +17,7 @@ version_badge = "![v$VERSION](https://img.shields.io/badge/version-v$VERSION-gre Markdown.parse("$github_badge $version_badge") ``` -Implementation of [Krotov's method of optimal control](https://arxiv.org/abs/1008.5126) enhanced with automatic differentiation. +Implementation of Krotov's method of optimal control [Krotov1996,SomloiCP1993,BartanaJCP1997,PalaoPRA2003,ReichJCP2012,GoerzSPP2019](@cite) enhanced with automatic differentiation [GoerzQ2022](@cite). Part of [`QuantumControl.jl`](https://github.com/JuliaQuantumControl/QuantumControl.jl#readme) and the [JuliaQuantumControl](https://github.com/JuliaQuantumControl) organization. @@ -26,44 +26,12 @@ Part of [`QuantumControl.jl`](https://github.com/JuliaQuantumControl/QuantumCont ## Contents -### Overview - ```@contents -Pages = [ - "overview.md", -] -Depth = 1 +Depth = 2 +Pages = [pair[2] for pair in Main.PAGES[2:end-1]] ``` -### Examples - -```@contents -Pages = [ - "examples/simple_state_to_state.md", - "examples/rho_3states.md", - "examples/state_to_state_parametrizations.md", - "examples/perfect_entanglers.md", -] -Depth = 1 -``` - -See also the [general examples](https://juliaquantumcontrol.github.io/QuantumControl.jl/stable/examples/) of the [QuantumControl](https://juliaquantumcontrol.github.io/QuantumControl.jl/stable/) package. - - -### API - -```@contents -Pages = [ - "api.md", -] -Depth = 1 -``` ## History See the [Releases](https://github.com/JuliaQuantumControl/Krotov.jl/releases) on Github. - -## References - -```@bibliography -``` diff --git a/docs/src/inventories/TimerOutputs.toml b/docs/src/inventories/TimerOutputs.toml new file mode 100644 index 0000000..60c4071 --- /dev/null +++ b/docs/src/inventories/TimerOutputs.toml @@ -0,0 +1,84 @@ +[Inventory] +format = "DocInventories v0" +project = "TimerOutputs.jl" +version = "0.5.23" + +[[std.doc]] +name = "readme" +uri = "" +dispname = "TimerOutputs" + +[[std.label]] +name = "timeroutputs" +uri = "#$" +dispname = "TimerOutputs" +[[std.label]] +name = "example-output" +uri = "#$" +dispname = "Example output" +[[std.label]] +name = "usage" +uri = "#$" +dispname = "Usage" +[[std.label]] +name = "settings-for-printing" +uri = "#$" +dispname = "Settings for printing" +[[std.label]] +name = "flattening" +uri = "#$" +dispname = "Flattening" +[[std.label]] +name = "merging" +uri = "#$" +dispname = "Merging" +[[std.label]] +name = "resetting" +uri = "#$" +dispname = "Resetting" +[[std.label]] +name = "indexing-into-a-table" +uri = "#$" +dispname = "Indexing into a table" +[[std.label]] +name = "querying-data" +uri = "#$" +dispname = "Querying data" +[[std.label]] +name = "default-timer" +uri = "#$" +dispname = "Default Timer" +[[std.label]] +name = "measuring-time-consumed-outside-timeit-blocks" +uri = "#$" +dispname = "Measuring time consumed outside @timeit blocks" +[[std.label]] +name = "shared-timers" +uri = "#$" +dispname = "Shared Timers" +[[std.label]] +name = "serialization" +uri = "#$" +dispname = "Serialization" +[[std.label]] +name = "overhead" +uri = "#$" +dispname = "Overhead" +[[std.label]] +name = "author" +uri = "#$" +dispname = "Autor" +[[std.label]] +name = "acknowledgments" +uri = "#$" +dispname = "Acknowledgments" + +[[jl.type]] +name = "TimerOutputs.TimerOutput" +uri = "#usage" +[[jl.function]] +name = "TimerOutputs.print_timer" +uri = "#settings-for-printing" +[[jl.function]] +name = "TimerOutputs.reset_timer!" +uri = "#resetting" diff --git a/docs/src/refs.bib b/docs/src/refs.bib index 2b203c1..8bf3847 100644 --- a/docs/src/refs.bib +++ b/docs/src/refs.bib @@ -168,3 +168,93 @@ @article{ChildsPRA2003 Pages = {052311}, Volume = {68}, } + +@book{Krotov1996, + Address = {New York, NY, USA}, + Author = {Krotov, Vadim F.}, + Publisher = {Dekker}, + Title = {Global Methods in Optimal Control}, + Year = {1996}, +} + + +@article{BartanaJCP1997, + Author = {Bartana, Allon and Kosloff, Ronnie and Tannor, David J.}, + Title = {Laser cooling of internal degrees of freedom. {II}}, + Journal = jcp, + Year = {1997}, + Doi = {10.1063/1.473973}, + Pages = {1435}, + Volume = {106}, +} + +@article{PalaoPRA2003, + Author = {Palao, José P. and Kosloff, Ronnie}, + Title = {Optimal control theory for unitary transformations}, + Journal = pra, + Year = {2003}, + Doi = {10.1103/PhysRevA.68.062308}, + Pages = {062308}, + Volume = {68}, +} + +@article{ReichJCP2012, + Author = {Reich, Daniel M. and Ndong, Mamadou and Koch, Christiane P.}, + Title = {Monotonically convergent optimization in quantum control using Krotov's method}, + Journal = jcp, + Year = {2012}, + Doi = {10.1063/1.3691827}, + Pages = {104103}, + Volume = {136}, +} + +@article{GoerzSPP2019, + Author = {Goerz, Michael H. and Basilewitsch, Daniel and Gago-Encinas, Fernando and Krauss, Matthias G. and Horn, Karl P. and Reich, Daniel M. and Koch, Christiane P.}, + Title = {Krotov: A {Python} implementation of Krotov's method for quantum optimal control}, + Journal = spp, + Year = {2019}, + Doi = {10.21468/scipostphys.7.6.080}, + Pages = {080}, + Volume = {7}, +} + +@article{SomloiCP1993, + Author = {Somlói, József and Kazakov, Vladimir A. and Tannor, David J.}, + Title = {Controlled dissociation of I$_2$ via optical transitions between the {X} and {B} electronic states}, + Journal = cp, + Year = {1993}, + Doi = {10.1016/0301-0104(93)80108-L}, + Pages = {85}, + Volume = {172}, +} + +@article{GoerzQ2022, + Author = {Goerz, Michael H. and Carrasco, Sebastián C. and Malinovsky, Vladimir S.}, + Title = {Quantum Optimal Control via Semi-Automatic Differentiation}, + Journal = quant, + Year = {2022}, + Doi = {10.22331/q-2022-12-07-871}, + Pages = {871}, + Volume = {6}, +} + +@article{CanevaPRA2011, + Author = {Caneva, Tommaso and Calarco, Tommaso and Montangero, Simone}, + Title = {Chopped random-basis quantum optimization}, + Journal = pra, + Year = {2011}, + Doi = {10.1103/physreva.84.022326}, + Pages = {022326}, + Volume = {84}, + Number = {2}, +} + +@article{MachnesPRL2018, + Author = {Machnes, Shai and Assémat, Elie and Tannor, David and Wilhelm, Frank K.}, + Title = {Tunable, Flexible, and Efficient Optimization of Control Pulses for Practical Qubits}, + Journal = prl, + Year = {2018}, + Doi = {10.1103/PhysRevLett.120.150401}, + Pages = {150401}, + Volume = {120}, +} diff --git a/examples/perfect_entanglers.jl b/examples/perfect_entanglers.jl deleted file mode 100644 index e999154..0000000 --- a/examples/perfect_entanglers.jl +++ /dev/null @@ -1,438 +0,0 @@ -# # Example 4: Optimization for a perfect entangler -#md # !!! tip -#md # This example is also available as a Jupyter notebook: -#md # [`perfect_entanglers.ipynb`](@__NBVIEWER_ROOT_URL__/examples/perfect_entanglers.ipynb). -#md # -#md # Compare this example against the [same example using GRAPE](https://juliaquantumcontrol.github.io/GRAPE.jl/stable/examples/perfect_entanglers/). - -#md # ``\gdef\op#1{\hat{#1}}`` -#md # ``\gdef\init{\text{init}}`` -#md # ``\gdef\tgt{\text{tgt}}`` - -#nb # $ -#nb # \newcommand{tr}[0]{\operatorname{tr}} -#nb # \newcommand{diag}[0]{\operatorname{diag}} -#nb # \newcommand{abs}[0]{\operatorname{abs}} -#nb # \newcommand{pop}[0]{\operatorname{pop}} -#nb # \newcommand{aux}[0]{\text{aux}} -#nb # \newcommand{opt}[0]{\text{opt}} -#nb # \newcommand{tgt}[0]{\text{tgt}} -#nb # \newcommand{init}[0]{\text{init}} -#nb # \newcommand{lab}[0]{\text{lab}} -#nb # \newcommand{rwa}[0]{\text{rwa}} -#nb # \newcommand{bra}[1]{\langle#1\vert} -#nb # \newcommand{ket}[1]{\vert#1\rangle} -#nb # \newcommand{Bra}[1]{\left\langle#1\right\vert} -#nb # \newcommand{Ket}[1]{\left\vert#1\right\rangle} -#nb # \newcommand{Braket}[2]{\left\langle #1\vphantom{#2}\mid{#2}\vphantom{#1}\right\rangle} -#nb # \newcommand{op}[1]{\hat{#1}} -#nb # \newcommand{Op}[1]{\hat{#1}} -#nb # \newcommand{dd}[0]{\,\text{d}} -#nb # \newcommand{Liouville}[0]{\mathcal{L}} -#nb # \newcommand{DynMap}[0]{\mathcal{E}} -#nb # \newcommand{identity}[0]{\mathbf{1}} -#nb # \newcommand{Norm}[1]{\lVert#1\rVert} -#nb # \newcommand{Abs}[1]{\left\vert#1\right\vert} -#nb # \newcommand{avg}[1]{\langle#1\rangle} -#nb # \newcommand{Avg}[1]{\left\langle#1\right\rangle} -#nb # \newcommand{AbsSq}[1]{\left\vert#1\right\vert^2} -#nb # \newcommand{Re}[0]{\operatorname{Re}} -#nb # \newcommand{Im}[0]{\operatorname{Im}} -#nb # $ - -const PROJECTDIR = dirname(Base.active_project()); -projectdir(names...) = joinpath(PROJECTDIR, names...); -datadir(names...) = projectdir("data", names...); -#jl using Test; println("") - -# This example illustrates the optimization towards a perfectly entangling -# two-qubit gate for a system of two transmon qubits with a shared transmission -# line. It uses both the indirect perfect entanglers functional shown in -# Goerz *et al.*, Phys. Rev. A 91, 062307 (2015) [GoerzPRA2015](@cite) and a -# direct maximization of the gate concurrence and thus demonstrates the -# optimization for non-analytic functions via the calculation of gradients with -# automatic differentiation. - -# ## Hamiltonian and guess pulses - -# We will write the Hamiltonian in units of GHz (angular frequency; the factor -# 2π is implicit) and ns: - -const GHz = 2π -const MHz = 0.001GHz -const ns = 1.0 -const μs = 1000ns; - -# The Hamiltonian and parameters are taken from -# Ref. [GoerzPRA2015; Table 1](@cite). - -⊗ = kron -const 𝕚 = 1im -const N = 6 # levels per transmon - -using LinearAlgebra -using SparseArrays -using QuantumControl - - -function transmon_hamiltonian(; - Ωre, - Ωim, - N=N, # levels per transmon - ω₁=4.380GHz, - ω₂=4.614GHz, - ωd=4.498GHz, - α₁=-210MHz, - α₂=-215MHz, - J=-3MHz, - λ=1.03, - use_sparse=:auto -) - 𝟙 = SparseMatrixCSC{ComplexF64,Int64}(sparse(I, N, N)) - b̂₁ = spdiagm(1 => complex.(sqrt.(collect(1:N-1)))) ⊗ 𝟙 - b̂₂ = 𝟙 ⊗ spdiagm(1 => complex.(sqrt.(collect(1:N-1)))) - b̂₁⁺ = sparse(b̂₁') - b̂₂⁺ = sparse(b̂₂') - n̂₁ = sparse(b̂₁' * b̂₁) - n̂₂ = sparse(b̂₂' * b̂₂) - n̂₁² = sparse(n̂₁ * n̂₁) - n̂₂² = sparse(n̂₂ * n̂₂) - b̂₁⁺_b̂₂ = sparse(b̂₁' * b̂₂) - b̂₁_b̂₂⁺ = sparse(b̂₁ * b̂₂') - - ω̃₁ = ω₁ - ωd - ω̃₂ = ω₂ - ωd - - Ĥ₀ = sparse( - (ω̃₁ - α₁ / 2) * n̂₁ + - (α₁ / 2) * n̂₁² + - (ω̃₂ - α₂ / 2) * n̂₂ + - (α₂ / 2) * n̂₂² + - J * (b̂₁⁺_b̂₂ + b̂₁_b̂₂⁺) - ) - - Ĥ₁re = (1 / 2) * (b̂₁ + b̂₁⁺ + λ * b̂₂ + λ * b̂₂⁺) - Ĥ₁im = (𝕚 / 2) * (b̂₁⁺ - b̂₁ + λ * b̂₂⁺ - λ * b̂₂) - - if ((N < 5) && (use_sparse ≢ true)) || use_sparse ≡ false - H = hamiltonian(Array(Ĥ₀), (Array(Ĥ₁re), Ωre), (Array(Ĥ₁im), Ωim)) - else - H = hamiltonian(Ĥ₀, (Ĥ₁re, Ωre), (Ĥ₁im, Ωim)) - end - return H - -end; - -# We choose a pulse duration of 400 ns. The guess pulse amplitude is 35 MHz, -# with a 15 ns switch-on/-off time. The Hamiltonian is written in a rotating -# frame, so in general, the control field is allowed to be complex-valued. We -# separate this into two control fields, one for the real part and one for the -# imaginary part. Initially, the imaginary part is zero, corresponding to a -# field exactly at the frequency of the rotating frame. - -using QuantumControl.Shapes: flattop - -function guess_pulses(; T=400ns, E₀=35MHz, dt=0.1ns, t_rise=15ns) - - tlist = collect(range(0, T, step=dt)) - Ωre(t) = E₀ * flattop(t, T=T, t_rise=t_rise) - Ωim(t) = 0.0 - - return tlist, Ωre, Ωim - -end - -tlist, Ωre_guess, Ωim_guess = guess_pulses(); - -# We can visualize this: - -using Plots -Plots.default( - linewidth = 3, - size = (550, 300), - legend = :right, - foreground_color_legend = nothing, - background_color_legend = RGBA(1, 1, 1, 0.8), -) - -function plot_complex_pulse(tlist, Ω; time_unit=:ns, ampl_unit=:MHz, kwargs...) - - ax1 = plot( - tlist ./ eval(time_unit), - abs.(Ω) ./ eval(ampl_unit); - label="|Ω|", - xlabel="time ($time_unit)", - ylabel="amplitude ($ampl_unit)", - kwargs... - ) - - ax2 = plot( - tlist ./ eval(time_unit), - angle.(Ω) ./ π; - label="ϕ(Ω)", - xlabel="time ($time_unit)", - ylabel="phase (π)" - ) - - plot(ax1, ax2, layout=(2, 1)) - -end - -plot_complex_pulse(tlist, Ωre_guess.(tlist) + 𝕚 * Ωim_guess.(tlist)) - -# ## Logical basis for two-qubit gates - -# For simplicity, we will be define the qubits in the *bare* basis, i.e. -# ignoring the static coupling $J$. - -function ket(i::Int64; N=N) - Ψ = zeros(ComplexF64, N) - Ψ[i+1] = 1 - return Ψ -end - -function ket(indices::Int64...; N=N) - Ψ = ket(indices[1]; N=N) - for i in indices[2:end] - Ψ = Ψ ⊗ ket(i; N=N) - end - return Ψ -end - -function ket(label::AbstractString; N=N) - indices = [parse(Int64, digit) for digit in label] - return ket(indices...; N=N) -end - -basis = [ket("00"), ket("01"), ket("10"), ket("11")]; - -# ## Defining the optimization problem - -# We define the optimization with one objective for each of the four basis -# states: - -H = transmon_hamiltonian(Ωre=Ωre_guess, Ωim=Ωim_guess); - -objectives = [Objective(; initial_state=Ψ, generator=H) for Ψ ∈ basis]; - -# Note that we omit the `target_state` here. This is because we will be -# optimizing for an arbitrary perfect entangler, not for a specific quantum -# gate. Thus, there is no a-priori known target state to which the initial -# state must evolve. - -# The optimization is steered by the perfect entanglers distance measure -# $D_{PE}$, that is, the geometric distance of the quantum gate obtained from -# propagating the four basis states to the polyhedron of perfect entanglers in -# the Weyl chamber. Since the logical subspace defining the qubit is embedded -# in the larger Hilbert space of the transmon, there may be loss of population -# from the logical subspace. To counter this possibility in the optimization, -# we add a unitarity measure to $D_{PE}$. The two terms are added with equal -# weight. - -using TwoQubitWeylChamber: D_PE, gate_concurrence, unitarity -using QuantumControl.Functionals: gate_functional - -J_T_PE = gate_functional(D_PE; unitarity_weight=0.5); - -# The `gate_functional` routines used above converts the function `D_PE` that -# receives the gate $Û$ as a 4×4 matrix into a functional of the correct from -# for the `QuantumControl.optimize` routine, which is a function of the -# propagated states. - -# We can check that for the guess pulse, we are not implementing a perfect -# entangler - -using QuantumControl: propagate_objectives -using QuantumPropagators: Cheby - -guess_states = propagate_objectives(objectives, tlist; method=Cheby, use_threads=true); - -U_guess = [basis[i] ⋅ guess_states[j] for i = 1:4, j = 1:4] - -gate_concurrence(U_guess) - -#jl @test gate_concurrence(U_guess) < 0.9 - -# We find that the guess pulse produces a gate in the `W0*` region of the Weyl -# chamber: - -using TwoQubitWeylChamber: weyl_chamber_region -weyl_chamber_region(U_guess) - -#jl @test weyl_chamber_region(U_guess) == "W0*" - -# That is, the region of the Weyl chamber containing controlled-phase gates with -# a phase $> π$ (Weyl chamber coordinates $c₁ > π/2$, $c₂ < π/4$). - -# This in fact allows use to use the perfect entangler functional without -# modification: if the guess pulse were in the "W1" region of the Weyl chamber, -# (close to SWAP), we would have to flip its sign, or we would optimize towards -# the local equivalence class of the SWAP gate instead of towards the perfect -# of perfect entanglers. In principle, we could use a modified functional that -# takes the absolute square of the `D_PE` term, by using -# -# ``` -# J_T_PE = gate_functional(D_PE; unitarity_weight=0.5, absolute_square=true) -# ``` -# -# This would specifically optimize for the *surface* of the perfect -# entanglers functional. - -# The guess pulse loses about 10% of population from the logical subspace: - -1 - unitarity(U_guess) - -#jl @test round(1 - unitarity(U_guess), digits=1) ≈ 0.1 - -# We can also evaluate the geometric distance to the polyhedron of perfect -# entanglers in the Weyl chamber: - -D_PE(U_guess) - -# Together with the unitarity measure, this is the initial value of the -# optimization functional: - -0.5 * D_PE(U_guess) + 0.5 * (1 - unitarity(U_guess)) -#- -J_T_PE(guess_states, objectives) - -#jl @test 0.4 < J_T_PE(guess_states, objectives) < 0.5 -#jl @test 0.5 * D_PE(U_guess) + 0.5 * (1-unitarity(U_guess)) ≈ J_T_PE(guess_states, objectives) atol=1e-15 - -# ## Optimization - -# Now, we formulate the full control problem - -problem = ControlProblem( - objectives=objectives, - lambda_a=10.0, - update_shape=(t -> flattop(t, T=400ns, t_rise=15ns, func=:blackman)), - tlist=tlist, - iter_stop=100, - J_T=J_T_PE, - check_convergence=res -> begin - ( - (res.J_T > res.J_T_prev) && - (res.converged = true) && - (res.message = "Loss of monotonic convergence") - ) - ( - (res.J_T <= 1e-3) && - (res.converged = true) && - (res.message = "Found a perfect entangler") - ) - end, - use_threads=true, -); - -# Note that we have not not given a `chi` parameter to calculate the boundary -# condition $|χₖ⟩ = -∂J_T/∂⟨ϕₖ|$ that Krotov's method requires. In this case, -# the Krotov.jl package will use automatic differentiation to determine the -# derivative. In principle, the perfect entanglers function has an analytical -# derivative, but it is exceedingly laborious to calculate and implement it -# (see the [source code of the `weylchamber` Python -# package](https://github.com/qucontrol/weylchamber/blob/master/src/weylchamber/perfect_entanglers.py), -# which can evaluate that derivative). - - -opt_result = @optimize_or_load(datadir("PE_OCT.jld2"), problem; method=:Krotov); -#- -opt_result - - -# ## Optimization result - -# We extract the optimized control field from the optimization result and plot -# it - -Ω_opt = opt_result.optimized_controls[1] + 𝕚 * opt_result.optimized_controls[2] - -plot_complex_pulse(tlist, Ω_opt) - -# We then propagate the optimized control field to analyze the resulting -# quantum gate: - -using QuantumControl.Controls: substitute, get_controls - -opt_states = propagate_objectives( - substitute( - objectives, - IdDict(zip(get_controls(objectives), opt_result.optimized_controls)) - ), - tlist; - method=Cheby, - use_threads=true -); - -U_opt = [basis[i] ⋅ opt_states[j] for i = 1:4, j = 1:4]; - -# We find that we have achieved a perfect entangler: - -gate_concurrence(U_opt) -#jl @test round(gate_concurrence(U_opt), digits=3) ≈ 1.0 - -# Moreover, we have reduced the population loss to ≈ 1% - -1 - unitarity(U_opt) - -#jl @test round(1 - unitarity(U_opt), digits=2) ≈ 0.01 - - -# ## Direct maximization of the gate concurrence - -# In the previous optimizations, we have optimized for a perfect entangler -# indirectly via a geometric function in the Weyl chamber. The entire reason -# that perfect entangler functional was formulated is because calculating the -# gate concurrence directly involves the eigenvalues of the unitary, see -# [KrausPRA2001](@citet) and [ChildsPRA2003](@citet), which are inherently -# non-analytic. - -# However, since Krotov.jl can use automatic differentiation, this is no longer -# an insurmountable obstacle! - -# We can define a functional for a given gate `U` that combines the gate -# concurrence and (as above) a unitarity measure to penalize loss of population -# from the logical subspace: - -J_T_C(U) = 0.5 * (1 - gate_concurrence(U)) + 0.5 * (1 - unitarity(U)); - -# In the optimization, we will convert this functional to one that takes the -# propagated states as arguments (via the `gate_functional` routine). -# We can do that same for the gradient: Let Zygote (the automatic -# differentiation framework we are using) determine the gradient for `J_T_C` -# with respect to `U`, but then analytically translate that into the derivative -# with respect to the states that we need to calculate the χ-states. - -using QuantumControl.Functionals: make_gate_chi - -chi_C = make_gate_chi(J_T_C, objectives); - -# Running this, we again are able to find a perfect entangler. - -opt_result_direct = @optimize_or_load( - datadir("PE_OCT_direct.jld2"), - problem; - method=:Krotov, - J_T=gate_functional(J_T_C), - chi=chi_C -); -#- -opt_result_direct -#- -opt_states_direct = propagate_objectives( - substitute( - objectives, - IdDict(zip(get_controls(objectives), opt_result_direct.optimized_controls)) - ), - tlist; - method=Cheby, - use_threads=true -); - -U_opt_direct = [basis[i] ⋅ opt_states_direct[j] for i = 1:4, j = 1:4]; -#- -gate_concurrence(U_opt_direct) -#jl @test round(gate_concurrence(U_opt_direct), digits=3) ≈ 1.0 -#- -1 - unitarity(U_opt_direct) -#jl @test round(1 - unitarity(U_opt_direct), digits=3) ≈ 0.002 diff --git a/examples/plots/positive_parametrization_comparison.jl b/examples/plots/positive_parametrization_comparison.jl deleted file mode 100644 index 0cf317c..0000000 --- a/examples/plots/positive_parametrization_comparison.jl +++ /dev/null @@ -1,120 +0,0 @@ -using QuantumControl.PulseParametrizations: - SquareParametrization, - TanhParametrization, - TanhSqParametrization, - LogisticParametrization, - LogisticSqParametrization - -using Plots -Plots.default( - linewidth = 3, - size = (550, 300), - legend = :top, - foreground_color_legend = nothing, - background_color_legend = RGBA(1, 1, 1, 0.8), -) - -function plot_positive_parametrization_comparison() - - u_vals = collect(range(-3, 3, length=101)) - ϵ_vals = collect(range(0, 1, length=101)) - ϵ_max = 1.0 - - pnl1 = plot( - u_vals, - abs.(u_vals); - linestyle=:dash, - color="black", - label="", - xlabel="u", - ylabel="ϵ", - legend=false - ) - plot!(pnl1, u_vals, TanhSqParametrization(ϵ_max).a_of_epsilon.(u_vals), label="TanhSq") - plot!( - pnl1, - u_vals, - LogisticSqParametrization(ϵ_max).a_of_epsilon.(u_vals), - label="LogisticSq(k=1)" - ) - plot!( - pnl1, - u_vals, - LogisticSqParametrization(ϵ_max, k=4.0).a_of_epsilon.(u_vals), - label="LogisticSq(k=4)" - ) - plot!(pnl1, u_vals, SquareParametrization().a_of_epsilon.(u_vals), label="Square") - ylims!(pnl1, (0, 1.2)) - - pnl2 = plot( - ϵ_vals, - ϵ_vals; - linestyle=:dash, - color="black", - label="", - xlabel="ϵ", - ylabel="u" - ) - plot!(pnl2, ϵ_vals, TanhSqParametrization(ϵ_max).epsilon_of_a.(ϵ_vals), label="TanhSq") - plot!( - pnl2, - ϵ_vals, - LogisticSqParametrization(ϵ_max).epsilon_of_a.(ϵ_vals), - label="LogisticSq(k=1)" - ) - plot!( - pnl2, - ϵ_vals, - LogisticSqParametrization(ϵ_max, k=4.0).epsilon_of_a.(ϵ_vals), - label="LogisticSq(k=4)" - ) - plot!(pnl2, ϵ_vals, SquareParametrization().epsilon_of_a.(ϵ_vals), label="Square") - ylims!(pnl2, (0, 3)) - - pnl3 = plot( - u_vals, - sign.(u_vals); - linestyle=:dash, - color="black", - label="", - xlabel="u", - ylabel="∂ϵ/∂u", - legend=false - ) - plot!( - pnl3, - u_vals, - TanhSqParametrization(ϵ_max).da_deps_derivative.(u_vals), - label="TanhSq" - ) - plot!( - pnl3, - u_vals, - LogisticSqParametrization(ϵ_max).da_deps_derivative.(u_vals), - label="LogisticSq(k=1)" - ) - plot!( - pnl3, - u_vals, - LogisticSqParametrization(ϵ_max, k=4.0).da_deps_derivative.(u_vals), - label="LogisticSq(k=4)" - ) - plot!(pnl3, u_vals, SquareParametrization().da_deps_derivative.(u_vals), label="Square") - ylims!(pnl3, (-2, 2)) - - plot( - pnl1, - pnl2, - pnl3, - layout=(1, 3), - size=(1000, 300), - left_margin=20Plots.px, - bottom_margin=20Plots.px - ) - -end - -if abspath(PROGRAM_FILE) == @__FILE__ - gui(plot_positive_parametrization_comparison()) - readline() -end diff --git a/examples/plots/symmetric_parametrization_comparison.jl b/examples/plots/symmetric_parametrization_comparison.jl deleted file mode 100644 index 2b4e235..0000000 --- a/examples/plots/symmetric_parametrization_comparison.jl +++ /dev/null @@ -1,129 +0,0 @@ -using QuantumControl.PulseParametrizations: - SquareParametrization, - TanhParametrization, - TanhSqParametrization, - LogisticParametrization, - LogisticSqParametrization - -using Plots -Plots.default( - linewidth = 3, - size = (550, 300), - legend = :top, - foreground_color_legend = nothing, - background_color_legend = RGBA(1, 1, 1, 0.8), -) - -function plot_symmetric_parametrization_comparison() - - u_vals = collect(range(-3, 3, length=101)) - ϵ_vals = collect(range(-1, 1, length=101)) - - ϵ_min = -1.0 - ϵ_max = 1.0 - - pnl1 = plot( - u_vals, - u_vals; - linestyle=:dash, - color="black", - label="", - xlabel="u", - ylabel="ϵ", - legend=false - ) - plot!( - pnl1, - u_vals, - TanhParametrization(ϵ_min, ϵ_max).a_of_epsilon.(u_vals), - label="Tanh" - ) - plot!( - pnl1, - u_vals, - LogisticParametrization(ϵ_min, ϵ_max).a_of_epsilon.(u_vals), - label="Logistic(k=1)" - ) - plot!( - pnl1, - u_vals, - LogisticParametrization(ϵ_min, ϵ_max, k=4).a_of_epsilon.(u_vals), - label="Logistic(k=4)" - ) - ylims!(pnl1, (-1.2, 1.2)) - - pnl2 = plot( - ϵ_vals, - ϵ_vals; - linestyle=:dash, - color="black", - label="", - xlabel="ϵ", - ylabel="u" - ) - plot!( - pnl2, - ϵ_vals, - TanhParametrization(ϵ_min, ϵ_max).epsilon_of_a.(ϵ_vals), - label="Tanh" - ) - plot!( - pnl2, - ϵ_vals, - LogisticParametrization(ϵ_min, ϵ_max).epsilon_of_a.(ϵ_vals), - label="Logistic(k=1)" - ) - plot!( - pnl2, - ϵ_vals, - LogisticParametrization(ϵ_min, ϵ_max, k=4).epsilon_of_a.(ϵ_vals), - label="Logistic(k=4)" - ) - ylims!(pnl2, (-3, 3)) - - pnl3 = plot( - u_vals, - [1.0 for _ in u_vals]; - linestyle=:dash, - color="black", - label="", - xlabel="u", - ylabel="∂ϵ/∂u", - legend=false - ) - plot!( - pnl3, - u_vals, - TanhParametrization(ϵ_min, ϵ_max).da_deps_derivative.(u_vals), - label="Tanh" - ) - plot!( - pnl3, - u_vals, - LogisticParametrization(ϵ_min, ϵ_max).da_deps_derivative.(u_vals), - label="Logistic(k=1)" - ) - plot!( - pnl3, - u_vals, - LogisticParametrization(ϵ_min, ϵ_max, k=4).da_deps_derivative.(u_vals), - label="Logistic(k=4)" - ) - ylims!(pnl3, (0, 2)) - - plot( - pnl1, - pnl2, - pnl3, - layout=(1, 3), - size=(1000, 300), - left_margin=20Plots.px, - bottom_margin=20Plots.px - ) - -end - -if abspath(PROGRAM_FILE) == @__FILE__ - gui(plot_symmetric_parametrization_comparison()) - readline() -end diff --git a/examples/rho_3states.jl b/examples/rho_3states.jl deleted file mode 100644 index 6d4d5a2..0000000 --- a/examples/rho_3states.jl +++ /dev/null @@ -1,305 +0,0 @@ -# # Example 2: Optimization of a Dissipative Quantum Gate - -#md # !!! tip -#md # This example is also available as a Jupyter notebook: -#md # [`rho_3states.ipynb`](@__NBVIEWER_ROOT_URL__/examples/rho_3states.ipynb). -#md # -#md # Compare this example against the [same example using the `krotov` -#md # Python package](https://qucontrol.github.io/krotov/v1.2.1/notebooks/06_example_3states.html). - -#md # ``\gdef\op#1{\hat{#1}}`` -#md # ``\gdef\ketbra#1#2{\vert#1\rangle\langle#2\vert}`` -#md # ``\gdef\init{\text{init}}`` -#md # ``\gdef\tgt{\text{tgt}}`` - -#nb # $ -#nb # \newcommand{tr}[0]{\operatorname{tr}} -#nb # \newcommand{diag}[0]{\operatorname{diag}} -#nb # \newcommand{abs}[0]{\operatorname{abs}} -#nb # \newcommand{pop}[0]{\operatorname{pop}} -#nb # \newcommand{aux}[0]{\text{aux}} -#nb # \newcommand{opt}[0]{\text{opt}} -#nb # \newcommand{tgt}[0]{\text{tgt}} -#nb # \newcommand{init}[0]{\text{init}} -#nb # \newcommand{lab}[0]{\text{lab}} -#nb # \newcommand{rwa}[0]{\text{rwa}} -#nb # \newcommand{bra}[1]{\langle#1\vert} -#nb # \newcommand{ket}[1]{\vert#1\rangle} -#nb # \newcommand{ketbra}[2]{\vert#1\rangle\langle#2\vert} -#nb # \newcommand{Bra}[1]{\left\langle#1\right\vert} -#nb # \newcommand{Ket}[1]{\left\vert#1\right\rangle} -#nb # \newcommand{Braket}[2]{\left\langle #1\vphantom{#2}\mid{#2}\vphantom{#1}\right\rangle} -#nb # \newcommand{op}[1]{\hat{#1}} -#nb # \newcommand{Op}[1]{\hat{#1}} -#nb # \newcommand{dd}[0]{\,\text{d}} -#nb # \newcommand{Liouville}[0]{\mathcal{L}} -#nb # \newcommand{DynMap}[0]{\mathcal{E}} -#nb # \newcommand{identity}[0]{\mathbf{1}} -#nb # \newcommand{Norm}[1]{\lVert#1\rVert} -#nb # \newcommand{Abs}[1]{\left\vert#1\right\vert} -#nb # \newcommand{avg}[1]{\langle#1\rangle} -#nb # \newcommand{Avg}[1]{\left\langle#1\right\rangle} -#nb # \newcommand{AbsSq}[1]{\left\vert#1\right\vert^2} -#nb # \newcommand{Re}[0]{\operatorname{Re}} -#nb # \newcommand{Im}[0]{\operatorname{Im}} -#nb # $ - - -# This example illustrates the optimization for a quantum gate in an open -# quantum system, where the dynamics is governed by the Liouville-von Neumann -# equation. - -const PROJECTDIR = dirname(Base.active_project()) -projectdir(names...) = joinpath(PROJECTDIR, names...) -datadir(names...) = projectdir("data", names...) - -using QuantumControl -using LinearAlgebra -using Serialization -using SparseArrays -#- -using Plots -Plots.default( - linewidth = 3, - size = (550, 300), - legend = :right, - foreground_color_legend = nothing, - background_color_legend = RGBA(1, 1, 1, 0.8), -) -#- - -#jl using Test; println("") - -# ## The two-transmon system - -# We will use internal units GHz and ns. Values in GHz contain an implicit -# factor $2 \pi$, and MHz and μs are converted to GHz and ns, respectively: - -const GHz = 2π; -const MHz = 0.001GHz; -const ns = 1.0; -const μs = 1000ns; -const 𝕚 = 1im; - -# This implicit factor $2 \pi$ is because frequencies ($\nu$) convert to -# energies as $E = h \nu$, but our propagation routines assume a unit $\hbar = -# 1$ for energies. Thus, the factor $h / \hbar = 2 \pi$. - - -function transmon_liouvillian( - Ωre, - Ωim; - N=5, # number of qubit levels - ω₁=4.3796GHz, # qubit frequency 1 - ω₂=4.6137GHz, # qubit frequency 2 - ωd=4.4985GHz, # drive frequency - δ₁=-239.3MHz, # anharmonicity 1 - δ₂=-242.8MHz, # anharmonicity 2 - J=-2.3MHz, # effective qubit-qubit coupling - γ₁₁=(1 / 38.0μs), # decay rate for qubit 1 - γ₁₂=(1 / 32.0μs), # decay rate for qubit 2 - γ₂₁=(1 / 29.5μs), # dephasing rate for qubit 1 - γ₂₂=(1 / 16.0μs) # dephasing time for qubit 2 -) - - ⊗(A, B) = kron(A, B) - 𝟙 = SparseMatrixCSC{ComplexF64,Int64}(sparse(I, N, N)) - - b̂₁ = spdiagm(1 => complex.(sqrt.(collect(1:N-1)))) ⊗ 𝟙 - b̂₂ = 𝟙 ⊗ spdiagm(1 => complex.(sqrt.(collect(1:N-1)))) - b̂₁⁺ = sparse(b̂₁') - b̂₂⁺ = sparse(b̂₂') - n̂₁ = sparse(b̂₁' * b̂₁) - n̂₂ = sparse(b̂₂' * b̂₂) - n̂₁² = sparse(n̂₁ * n̂₁) - n̂₂² = sparse(n̂₂ * n̂₂) - b̂₁⁺_b̂₂ = sparse(b̂₁' * b̂₂) - b̂₁_b̂₂⁺ = sparse(b̂₁ * b̂₂') - - Ĥ₀ = sparse( - (ω₁ - ωd - δ₁ / 2) * n̂₁ + - (δ₁ / 2) * n̂₁² + - (ω₂ - ωd - δ₂ / 2) * n̂₂ + - (δ₂ / 2) * n̂₂² + - J * (b̂₁⁺_b̂₂ + b̂₁_b̂₂⁺) - ) - - Ĥ₁re = (1 / 2) * (b̂₁ + b̂₁⁺ + b̂₂ + b̂₂⁺) - Ĥ₁im = (𝕚 / 2) * (b̂₁⁺ - b̂₁ + b̂₂⁺ - b̂₂) - - H = (Ĥ₀, (Ĥ₁re, Ωre), (Ĥ₁im, Ωim)) - - c_ops = [√γ₁₁ * b̂₁, √γ₁₂ * b̂₂, √γ₂₁ * n̂₁, √γ₂₂ * n̂₂] - - return liouvillian(H, c_ops; convention=:TDSE) - -end - -const T = 400ns; - -Ωre(t) = 35MHz * QuantumControl.Shapes.flattop(t; T=T, t_rise=20ns); -Ωim(t) = 0.0; - -L = transmon_liouvillian(Ωre, Ωim); - -tlist = collect(range(0, 400ns, length=2000)); - -# The guess pulse looks as follows: - -function plot_control(pulse::Vector, tlist) - plot(tlist, pulse, xlabel="time", ylabel="amplitude", legend=false) -end - -plot_control(ϵ::Function, tlist) = plot_control([ϵ(t) for t in tlist], tlist); -#- -fig = plot_control(Ωre, tlist) -#jl display(fig) - - -# ## Optimization objectives - -# Our target gate is $\op{O} = \sqrt{\text{iSWAP}}$: - -SQRTISWAP = [ - 1 0 0 0 - 0 1/√2 𝕚/√2 0 - 0 𝕚/√2 1/√2 0 - 0 0 0 1 -]; - -# The key idea explored in the paper is that a set of three density matrices is sufficient to track the optimization -# -# ```math -# \begin{align} -# \op{\rho}_1 -# &= \sum_{i=1}^{d} \frac{2 (d-i+1)}{d (d+1)} \ketbra{i}{i} \\ -# \op{\rho}_2 -# &= \sum_{i,j=1}^{d} \frac{1}{d} \ketbra{i}{j} \\ -# \op{\rho}_3 -# &= \sum_{i=1}^{d} \frac{1}{d} \ketbra{i}{i} -# \end{align} -# ``` - -# In our case, $d=4$ for a two qubit-gate, and the $\ket{i}$, $\ket{j}$ are the canonical basis states $\ket{00}$, $\ket{01}$, $\ket{10}$, $\ket{11}$ - -function ket(i::Int64; N=5) - Ψ = zeros(ComplexF64, N) - Ψ[i+1] = 1 - return Ψ -end; - -ket(i::Int64, j::Int64; N=5) = kron(ket(i; N=N), ket(j; N=N)); - -bra(args...; N=5) = adjoint(ket(args..., N=N)); - -const basis_labels = [(0, 0), (0, 1), (1, 0), (1, 1)]; -const basis = [ket(labels...) for labels in basis_labels]; -const d = length(basis); - -const basis_tgt = [sum([SQRTISWAP[i, j] * basis[i] for i ∈ 1:d]) for j ∈ 1:d]; - - -const ρ̂₁ = - sum([(2 * (d - i + 1) / (d * (d + 1))) * basis[i] * adjoint(basis[i]) for i ∈ 1:d]); -const ρ̂₂ = sum([(1 / d) * basis[i] * adjoint(basis[j]) for i ∈ 1:d for j ∈ 1:d]); -const ρ̂₃ = sum([(1 / d) * basis[i] * adjoint(basis[i]) for i ∈ 1:d]); - -const ρ̂₁_tgt = sum([ - (2 * (d - i + 1) / (d * (d + 1))) * basis_tgt[i] * adjoint(basis_tgt[i]) for i ∈ 1:d -]); -const ρ̂₂_tgt = - sum([(1 / d) * basis_tgt[i] * adjoint(basis_tgt[j]) for i ∈ 1:d for j ∈ 1:d]); -const ρ̂₃_tgt = sum([(1 / d) * basis_tgt[i] * adjoint(basis_tgt[i]) for i ∈ 1:d]); - - -# The three density matrices play different roles in the optimization, and, as -# shown in the paper, convergence may improve significantly by weighing the -# states relatively to each other. For this example, we place a strong emphasis -# on the optimization $\op{\rho}_1 \rightarrow \op{O}^\dagger \op{\rho}_1 -# \op{O}$, by a factor of 20. This reflects that the hardest part of the -# optimization is identifying the basis in which the gate is diagonal. We will -# be using the real-part functional ($J_{T,\text{re}}$) to evaluate the success -# of $\op{\rho}_i \rightarrow \op{O}\op{\rho}_i\op{O}^\dagger$. Because -# $\op{\rho}_1$ and $\op{\rho}_3$ are mixed states, the Hilbert-Schmidt overlap -# will take values smaller than one in the optimal case. To compensate, we -# divide the weights by the purity of the respective states. -# -weights = Float64[20, 1, 1]; -weights *= length(weights) / sum(weights); # manual normalization -weights ./= [0.3, 1.0, 0.25]; # purities - -const objectives = [ - Objective( - initial_state=reshape(ρ̂₁, :), - generator=L, - target_state=reshape(ρ̂₁_tgt, :), - weight=weights[1] - ), - Objective( - initial_state=reshape(ρ̂₂, :), - generator=L, - target_state=reshape(ρ̂₂_tgt, :), - weight=weights[2] - ), - Objective( - initial_state=reshape(ρ̂₃, :), - generator=L, - target_state=reshape(ρ̂₃_tgt, :), - weight=weights[3] - ) -]; - - -# ## Dynamics under the Guess Pulse - -ρ̂₀₀ = ket(0, 0) * adjoint(ket(0, 0)); -ρ̂₀₁ = ket(0, 1) * adjoint(ket(0, 1)); -ρ̂₁₀ = ket(1, 0) * adjoint(ket(1, 0)); -ρ̂₁₁ = ket(1, 1) * adjoint(ket(1, 1)); - -function as_matrix(ρ⃗) - N = isqrt(length(ρ⃗)) - return reshape(ρ⃗, N, N) -end; - -pop00(ρ⃗) = real(tr(as_matrix(ρ⃗) * ρ̂₀₀)); -pop01(ρ⃗) = real(tr(as_matrix(ρ⃗) * ρ̂₀₁)); -pop10(ρ⃗) = real(tr(as_matrix(ρ⃗) * ρ̂₁₀)); -pop11(ρ⃗) = real(tr(as_matrix(ρ⃗) * ρ̂₁₁)); - - -using QuantumPropagators: Newton - -rho_00_expvals = propagate_objective( - objectives[1], - tlist; - initial_state=reshape(ρ̂₀₀, :), - method=Newton, - observables=(pop00, pop01, pop10, pop11), - storage=true -); - - -# ## Optimization - - -const problem = ControlProblem( - objectives=objectives, - prop_method=Newton, - use_threads=true, - lambda_a=1.0, - update_shape=(t -> QuantumControl.Shapes.flattop(t, T=T, t_rise=20ns, func=:blackman)), - tlist=tlist, - iter_stop=3000, - J_T=QuantumControl.Functionals.J_T_re, - check_convergence=res -> begin - ((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³")) - end -); -#- -opt_result = @optimize_or_load( - datadir("DissGateOCT#J_T=J_T_re#iter_stop=3000#method=krotov.jld2"), - problem, - method = :krotov -) -#- diff --git a/examples/simple_state_to_state.jl b/examples/simple_state_to_state.jl deleted file mode 100644 index 411dbae..0000000 --- a/examples/simple_state_to_state.jl +++ /dev/null @@ -1,230 +0,0 @@ -# # Example 1: Optimization of a State-to-State Transfer in a Two-Level-System - -#md # !!! tip -#md # This example is also available as a Jupyter notebook: -#md # [`simple_state_to_state.ipynb`](@__NBVIEWER_ROOT_URL__/examples/simple_state_to_state.ipynb). -#md # -#md # Compare this example against the [same example using the `krotov` -#md # Python package](https://qucontrol.github.io/krotov/v1.2.1/notebooks/01_example_simple_state_to_state.html). - -#md # ``\gdef\op#1{\hat{#1}}`` -#md # ``\gdef\init{\text{init}}`` -#md # ``\gdef\tgt{\text{tgt}}`` - -#nb # $ -#nb # \newcommand{tr}[0]{\operatorname{tr}} -#nb # \newcommand{diag}[0]{\operatorname{diag}} -#nb # \newcommand{abs}[0]{\operatorname{abs}} -#nb # \newcommand{pop}[0]{\operatorname{pop}} -#nb # \newcommand{aux}[0]{\text{aux}} -#nb # \newcommand{opt}[0]{\text{opt}} -#nb # \newcommand{tgt}[0]{\text{tgt}} -#nb # \newcommand{init}[0]{\text{init}} -#nb # \newcommand{lab}[0]{\text{lab}} -#nb # \newcommand{rwa}[0]{\text{rwa}} -#nb # \newcommand{bra}[1]{\langle#1\vert} -#nb # \newcommand{ket}[1]{\vert#1\rangle} -#nb # \newcommand{Bra}[1]{\left\langle#1\right\vert} -#nb # \newcommand{Ket}[1]{\left\vert#1\right\rangle} -#nb # \newcommand{Braket}[2]{\left\langle #1\vphantom{#2}\mid{#2}\vphantom{#1}\right\rangle} -#nb # \newcommand{op}[1]{\hat{#1}} -#nb # \newcommand{Op}[1]{\hat{#1}} -#nb # \newcommand{dd}[0]{\,\text{d}} -#nb # \newcommand{Liouville}[0]{\mathcal{L}} -#nb # \newcommand{DynMap}[0]{\mathcal{E}} -#nb # \newcommand{identity}[0]{\mathbf{1}} -#nb # \newcommand{Norm}[1]{\lVert#1\rVert} -#nb # \newcommand{Abs}[1]{\left\vert#1\right\vert} -#nb # \newcommand{avg}[1]{\langle#1\rangle} -#nb # \newcommand{Avg}[1]{\left\langle#1\right\rangle} -#nb # \newcommand{AbsSq}[1]{\left\vert#1\right\vert^2} -#nb # \newcommand{Re}[0]{\operatorname{Re}} -#nb # \newcommand{Im}[0]{\operatorname{Im}} -#nb # $ - - -# This first example illustrates the basic use of the `Krotov.jl` by solving a -# simple canonical optimization problem: the transfer of population in a two -# level system. - -const PROJECTDIR = dirname(Base.active_project()) -projectdir(names...) = joinpath(PROJECTDIR, names...) -datadir(names...) = projectdir("data", names...) - -using QuantumControl -using QuantumPropagators.Controls: substitute -using QuantumPropagators: ExpProp -using LinearAlgebra -#- -using Plots -Plots.default( - linewidth = 3, - size = (550, 300), - legend = :right, - foreground_color_legend = nothing, - background_color_legend = RGBA(1, 1, 1, 0.8), -) -#- - -#jl using Test; println("") - -# ## Two-level Hamiltonian - -# We consider the Hamiltonian $\op{H}_{0} = - \frac{\omega}{2} \op{\sigma}_{z}$, representing -# a simple qubit with energy level splitting $\omega$ in the basis -# $\{\ket{0},\ket{1}\}$. The control field $\epsilon(t)$ is assumed to couple via -# the Hamiltonian $\op{H}_{1}(t) = \epsilon(t) \op{\sigma}_{x}$ to the qubit, -# i.e., the control field effectively drives transitions between both qubit -# states. -# -# We we will use - -ϵ(t) = 0.2 * QuantumControl.Shapes.flattop(t, T=5, t_rise=0.3, func=:blackman); - - -#- -"""Two-level-system Hamiltonian.""" -function tls_hamiltonian(Ω=1.0, ϵ=ϵ) - σ̂_z = ComplexF64[ - 1 0 - 0 -1 - ] - σ̂_x = ComplexF64[ - 0 1 - 1 0 - ] - Ĥ₀ = -0.5 * Ω * σ̂_z - Ĥ₁ = σ̂_x - return hamiltonian(Ĥ₀, (Ĥ₁, ϵ)) -end; -#- - -H = tls_hamiltonian(); -#jl @test length(H.ops) == 2 - -# The control field here switches on from zero at $t=0$ to it's maximum amplitude -# 0.2 within the time period 0.3 (the switch-on shape is half a [Blackman pulse](https://en.wikipedia.org/wiki/Window_function#Blackman_window)). -# It switches off again in the time period 0.3 before the -# final time $T=5$). We use a time grid with 500 time steps between 0 and $T$: - -tlist = collect(range(0, 5, length=500)); - -#- -function plot_control(pulse::Vector, tlist) - plot(tlist, pulse, xlabel="time", ylabel="amplitude", legend=false) -end - -plot_control(ϵ::Function, tlist) = plot_control([ϵ(t) for t in tlist], tlist); -#- -fig = plot_control(ϵ, tlist) -#jl display(fig) - -# ## Optimization target - -# The `krotov` package requires the goal of the optimization to be described by a -# list of `Objective` instances. In this example, there is only a single -# objective: the state-to-state transfer from initial state $\ket{\Psi_{\init}} = -# \ket{0}$ to the target state $\ket{\Psi_{\tgt}} = \ket{1}$, under the dynamics -# of the Hamiltonian $\op{H}(t)$: - -function ket(label) - result = Dict("0" => Vector{ComplexF64}([1, 0]), "1" => Vector{ComplexF64}([0, 1]),) - return result[string(label)] -end; - -#- -#jl @test dot(ket(0), ket(1)) ≈ 0 -#- - -objectives = [Objective(initial_state=ket(0), generator=H, target_state=ket(1))] - -#- -#jl @test length(objectives) == 1 -#- - -problem = ControlProblem( - objectives=objectives, - lambda_a=5, - update_shape=(t -> QuantumControl.Shapes.flattop(t, T=5, t_rise=0.3, func=:blackman)), - tlist=tlist, - iter_stop=50, - prop_method=ExpProp, - J_T=QuantumControl.Functionals.J_T_ss, - check_convergence=res -> begin - ((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³")) - end -); - -# ## Simulate dynamics under the guess field - -# Before running the optimization procedure, we first simulate the dynamics under the -# guess field $\epsilon_{0}(t)$. The following solves equation of motion for the -# defined objective, which contains the initial state $\ket{\Psi_{\init}}$ and -# the Hamiltonian $\op{H}(t)$ defining its evolution. - -guess_dynamics = propagate_objective( - objectives[1], - problem.tlist; - method=ExpProp, - storage=true, - observables=(Ψ -> abs.(Ψ) .^ 2,) -) - -#- -function plot_population(pop0::Vector, pop1::Vector, tlist) - fig = plot(tlist, pop0, label="0", xlabel="time", ylabel="population") - plot!(fig, tlist, pop1; label="1") -end; -#- -fig = plot_population(guess_dynamics[1, :], guess_dynamics[2, :], tlist) -#jl display(fig) - -# ## Optimize - -# In the following we optimize the guess field $\epsilon_{0}(t)$ such -# that the intended state-to-state transfer $\ket{\Psi_{\init}} \rightarrow -# \ket{\Psi_{\tgt}}$ is solved, via `optimize` routine. -# It requires, besides the previously defined -# `objectives`, information about the optimization functional $J_T$. - -opt_result = @optimize_or_load( - datadir("TLSOCT#J_T=J_T_ss#iter_stop=50#method=krotov.jld2"), - problem, - method = :krotov, -); -#- -opt_result -#- -#jl @test opt_result.J_T < 1e-3 -#- - -# We can plot the optimized field: - -#- -fig = plot_control(opt_result.optimized_controls[1], tlist) -#jl display(fig) -#- - -# ## Simulate the dynamics under the optimized field - -# Having obtained the optimized control field, we can simulate the dynamics to -# verify that the optimized field indeed drives the initial state -# $\ket{\Psi_{\init}} = \ket{0}$ to the desired target state -# $\ket{\Psi_{\tgt}} = \ket{1}$. - -opt_dynamics = propagate_objective( - substitute(objectives[1], IdDict(ϵ => opt_result.optimized_controls[1])), - problem.tlist; - method=ExpProp, - storage=true, - observables=(Ψ -> abs.(Ψ) .^ 2,) -) - -#- -fig = plot_population(opt_dynamics[1, :], opt_dynamics[2, :], tlist) -#jl display(fig) -#- - -#- -#jl @test opt_dynamics[2,end] > 0.99 -#- diff --git a/examples/state_to_state_parametrizations.jl b/examples/state_to_state_parametrizations.jl deleted file mode 100644 index 1c71314..0000000 --- a/examples/state_to_state_parametrizations.jl +++ /dev/null @@ -1,345 +0,0 @@ -# # Example 3: Pulse Parametrization - -#md # !!! tip -#md # This example is also available as a Jupyter notebook: -#md # [`state_to_state_parametrizations.ipynb`](@__NBVIEWER_ROOT_URL__/examples/state_to_state_parametrizations.ipynb). - -#md # ``\gdef\op#1{\hat{#1}}`` -#md # ``\gdef\init{\text{init}}`` -#md # ``\gdef\tgt{\text{tgt}}`` - -#nb # $ -#nb # \newcommand{tr}[0]{\operatorname{tr}} -#nb # \newcommand{diag}[0]{\operatorname{diag}} -#nb # \newcommand{abs}[0]{\operatorname{abs}} -#nb # \newcommand{pop}[0]{\operatorname{pop}} -#nb # \newcommand{aux}[0]{\text{aux}} -#nb # \newcommand{opt}[0]{\text{opt}} -#nb # \newcommand{tgt}[0]{\text{tgt}} -#nb # \newcommand{init}[0]{\text{init}} -#nb # \newcommand{lab}[0]{\text{lab}} -#nb # \newcommand{rwa}[0]{\text{rwa}} -#nb # \newcommand{bra}[1]{\langle#1\vert} -#nb # \newcommand{ket}[1]{\vert#1\rangle} -#nb # \newcommand{Bra}[1]{\left\langle#1\right\vert} -#nb # \newcommand{Ket}[1]{\left\vert#1\right\rangle} -#nb # \newcommand{Braket}[2]{\left\langle #1\vphantom{#2}\mid{#2}\vphantom{#1}\right\rangle} -#nb # \newcommand{op}[1]{\hat{#1}} -#nb # \newcommand{Op}[1]{\hat{#1}} -#nb # \newcommand{dd}[0]{\,\text{d}} -#nb # \newcommand{Liouville}[0]{\mathcal{L}} -#nb # \newcommand{DynMap}[0]{\mathcal{E}} -#nb # \newcommand{identity}[0]{\mathbf{1}} -#nb # \newcommand{Norm}[1]{\lVert#1\rVert} -#nb # \newcommand{Abs}[1]{\left\vert#1\right\vert} -#nb # \newcommand{avg}[1]{\langle#1\rangle} -#nb # \newcommand{Avg}[1]{\left\langle#1\right\rangle} -#nb # \newcommand{AbsSq}[1]{\left\vert#1\right\vert^2} -#nb # \newcommand{Re}[0]{\operatorname{Re}} -#nb # \newcommand{Im}[0]{\operatorname{Im}} -#nb # $ - - -# This example illustrates the parametrization of control pulses as a -# form of amplitude constraint. - -const PROJECTDIR = dirname(Base.active_project()) -projectdir(names...) = joinpath(PROJECTDIR, names...) -datadir(names...) = projectdir("data", names...) - -using QuantumControl -using QuantumControl.Shapes: flattop -using QuantumControl.Generators -using QuantumControl.Controls -using QuantumControl.PulseParametrizations: - SquareParametrization, - TanhParametrization, - TanhSqParametrization, - LogisticParametrization, - LogisticSqParametrization, - ParametrizedAmplitude -using LinearAlgebra - -#- -#jl using Test; println("") -using Plots -Plots.default( - linewidth = 3, - size = (550, 300), - legend = :top, - foreground_color_legend = nothing, - background_color_legend = RGBA(1, 1, 1, 0.8), -) -#- - -# ## Parametrizations - -# ## Symmetric Bounded Controls - -#- -include(joinpath(@__DIR__, "plots", "symmetric_parametrization_comparison.jl")) # hide -fig = plot_symmetric_parametrization_comparison() # hide -#jl display(fig) - -# ## Positive (Bounded) Controls - -#- -include(joinpath(@__DIR__, "plots", "positive_parametrization_comparison.jl")) # hide -fig = plot_positive_parametrization_comparison() # hide -#jl display(fig) - -# ## Two-level Hamiltonian - -# We consider the Hamiltonian $\op{H}_{0} = - \frac{\omega}{2} \op{\sigma}_{z}$, representing -# a simple qubit with energy level splitting $\omega$ in the basis -# $\{\ket{0},\ket{1}\}$. The control field $\epsilon(t)$ is assumed to couple via -# the Hamiltonian $\op{H}_{1}(t) = \epsilon(t) \op{\sigma}_{x}$ to the qubit, -# i.e., the control field effectively drives transitions between both qubit -# states. -# -# We we will use - -ϵ(t) = 0.2 * flattop(t, T=5, t_rise=0.3, func=:blackman); - - -#- -"""Two-level-system Hamiltonian.""" -function tls_hamiltonian(; Ω=1.0, ampl=ϵ) - σ̂_z = ComplexF64[ - 1 0 - 0 -1 - ] - σ̂_x = ComplexF64[ - 0 1 - 1 0 - ] - Ĥ₀ = -0.5 * Ω * σ̂_z - Ĥ₁ = σ̂_x - return hamiltonian(Ĥ₀, (Ĥ₁, ampl)) -end; -#- - -H = tls_hamiltonian(); -#jl @test length(H.ops) == 2 - -# The control field here switches on from zero at $t=0$ to it's maximum amplitude -# 0.2 within the time period 0.3 (the switch-on shape is half a [Blackman pulse](https://en.wikipedia.org/wiki/Window_function#Blackman_window)). -# It switches off again in the time period 0.3 before the -# final time $T=5$). We use a time grid with 500 time steps between 0 and $T$: - -tlist = collect(range(0, 5, length=500)); - -#- -function plot_amplitude(ampl, tlist) - plot(tlist, discretize(ampl, tlist), xlabel="time", ylabel="amplitude", legend=false) -end - -plot_amplitude(ϵ, tlist) - -# ## Optimization target - -# The `krotov` package requires the goal of the optimization to be described by a -# list of `Objective` instances. In this example, there is only a single -# objective: the state-to-state transfer from initial state $\ket{\Psi_{\init}} = -# \ket{0}$ to the target state $\ket{\Psi_{\tgt}} = \ket{1}$, under the dynamics -# of the Hamiltonian $\op{H}(t)$: - -function ket(label) - result = Dict("0" => Vector{ComplexF64}([1, 0]), "1" => Vector{ComplexF64}([0, 1]),) - return result[string(label)] -end; - -#- -#jl @test dot(ket(0), ket(1)) ≈ 0 -#- - -objectives = [Objective(initial_state=ket(0), generator=H, target_state=ket(1))] - -#- -#jl @test length(objectives) == 1 -#- - - -# ## Square-parametrization for positive pulses - -a = ParametrizedAmplitude( - ϵ, - tlist; - parametrization=SquareParametrization(), - parameterize=true -) - -#- -function plot_amplitude(ampl::ParametrizedAmplitude, tlist) - plot( - tlist, - discretize(Array(ampl), tlist), - xlabel="time", - ylabel="amplitude", - legend=false - ) -end - -plot_amplitude(a, tlist) - -#- -problem = ControlProblem( - objectives=substitute(objectives, IdDict(ϵ => a)), - lambda_a=5, - update_shape=(t -> flattop(t, T=5, t_rise=0.3, func=:blackman)), - tlist=tlist, - iter_stop=50, - J_T=QuantumControl.Functionals.J_T_ss, - check_convergence=res -> begin - ((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³")) - end -); -#- -opt_result_positive = @optimize_or_load( - datadir("parametrization#opt_result_positive.jld2"), - problem; - method=:krotov -); -#- -opt_result_positive - -# We can plot the optimized field: - -#- -#!jl plot_amplitude( -#!jl substitute(a, IdDict(a.control => opt_result_positive.optimized_controls[1])), -#!jl tlist -#!jl ) -#- - -#- -#jl amplitude = Array(substitute(a, IdDict(a.control => opt_result_positive.optimized_controls[1]))) -#jl @test minimum(amplitude) ≥ 0.0 -#jl @test minimum(amplitude) < 1e-16 -#jl @test maximum(amplitude) > 0.0 -#- - -# ## Tanh-Square-Parametrization for positive amplitude-constrained pulses - -a = ParametrizedAmplitude( - ϵ, - tlist; - parametrization=TanhSqParametrization(3), - parameterize=true -) - -problem_tanhsq = ControlProblem( - objectives=substitute(objectives, IdDict(ϵ => a)), - lambda_a=10, - update_shape=(t -> flattop(t, T=5, t_rise=0.3, func=:blackman)), - tlist=tlist, - iter_stop=50, - J_T=QuantumControl.Functionals.J_T_ss, - check_convergence=res -> begin - ((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³")) - end -); -#- -opt_result_tanhsq = @optimize_or_load( - datadir("parametrization#opt_result_tanhsq.jld2"), - problem_tanhsq; - method=:krotov -); -#- -opt_result_tanhsq - -# We can plot the optimized field: - -#- -#!jl plot_amplitude( -#!jl substitute(a, IdDict(a.control => opt_result_tanhsq.optimized_controls[1])), -#!jl tlist -#!jl ) -#- - -#- -#jl amplitude = Array(substitute(a, IdDict(a.control => opt_result_tanhsq.optimized_controls[1]))) -#jl @test minimum(amplitude) ≥ 0.0 -#jl @test minimum(amplitude) < 1e-16 -#jl @test maximum(amplitude) > 0.0 -#jl @test maximum(opt_result_tanhsq.optimized_controls[1]) < 3.0 -#- - -# ## Logistic-Square-Parametrization for positive amplitude-constrained pulses - -a = ParametrizedAmplitude( - ϵ, - tlist; - parametrization=LogisticSqParametrization(3, k=1.0), - parameterize=true -) - -problem_logisticsq = ControlProblem( - objectives=substitute(objectives, IdDict(ϵ => a)), - lambda_a=1, - update_shape=(t -> flattop(t, T=5, t_rise=0.3, func=:blackman)), - tlist=tlist, - iter_stop=50, - J_T=QuantumControl.Functionals.J_T_ss, - check_convergence=res -> begin - ((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³")) - end -); -#- -opt_result_logisticsq = @optimize_or_load( - datadir("parametrization#opt_result_logisticsq.jld2"), - problem_logisticsq; - method=:krotov -); -# We can plot the optimized field: - -#- -#!jl plot_amplitude( -#!jl substitute(a, IdDict(a.control => opt_result_logisticsq.optimized_controls[1])), -#!jl tlist -#!jl ) -#- - -#- -#jl amplitude = Array(substitute(a, IdDict(a.control => opt_result_logisticsq.optimized_controls[1]))) -#jl @test minimum(amplitude) ≥ 0.0 -#jl @test minimum(amplitude) < 1e-16 -#jl @test maximum(amplitude) > 0.0 -#jl @test maximum(amplitude) < 3.0 -#- -# ## Tanh-parametrization for amplitude-constrained pulses - -a = ParametrizedAmplitude( - ϵ, - tlist; - parametrization=TanhParametrization(-0.5, 0.5), - parameterize=true -) - -problem_tanh = ControlProblem( - objectives=substitute(objectives, IdDict(ϵ => a)), - lambda_a=1, - update_shape=(t -> flattop(t, T=5, t_rise=0.3, func=:blackman)), - tlist=tlist, - iter_stop=50, - J_T=QuantumControl.Functionals.J_T_ss, - check_convergence=res -> begin - ((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³")) - end -); -#- -opt_result_tanh = @optimize_or_load( - datadir("parametrization#opt_result_tanh.jld2"), - problem_tanh; - method=:krotov -); -#- -#!jl plot_amplitude( -#!jl substitute(a, IdDict(a.control => opt_result_tanh.optimized_controls[1])), -#!jl tlist -#!jl ) -#- -#jl amplitude = Array(substitute(a, IdDict(a.control => opt_result_tanh.optimized_controls[1]))) -#jl @test minimum(amplitude) > -0.5 -#jl @test maximum(amplitude) < 0.5 -#- diff --git a/test/clean.jl b/test/clean.jl index 5338f6a..c79c599 100644 --- a/test/clean.jl +++ b/test/clean.jl @@ -19,14 +19,6 @@ function clean(; distclean=false, _exit=true) ########################################################################### CLEAN = String[] - for folder in ["", "src", "test", "examples"] - append!(CLEAN, _glob(joinpath(ROOT, folder), ".cov")) - end - append!(CLEAN, _glob_star(joinpath(ROOT, "test", "examples"))) - append!( - CLEAN, - _glob_star(joinpath(ROOT, "docs", "src", "examples"), except=["index.md"]) - ) _push!(CLEAN, joinpath(ROOT, "coverage")) _push!(CLEAN, joinpath(ROOT, "docs", "build")) append!(CLEAN, _glob(ROOT, ".info")) @@ -39,9 +31,6 @@ function clean(; distclean=false, _exit=true) _push!(DISTCLEAN, joinpath(joinpath(ROOT, folder), "Manifest.toml")) end _push!(DISTCLEAN, joinpath(ROOT, "docs", "Project.toml")) - _push!(DISTCLEAN, joinpath(ROOT, "docs", "src", "examples", ".ipynb_checkpoints")) - append!(DISTCLEAN, _glob_star(joinpath(ROOT, "test", "data"))) - append!(DISTCLEAN, _glob_star(joinpath(ROOT, "docs", "data"))) _push!(DISTCLEAN, joinpath(ROOT, ".JuliaFormatter.toml")) ########################################################################### diff --git a/test/data/.gitignore b/test/data/.gitignore deleted file mode 100644 index d390084..0000000 --- a/test/data/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.jld2 diff --git a/test/download_dumps.jl b/test/download_dumps.jl deleted file mode 100644 index f42b67a..0000000 --- a/test/download_dumps.jl +++ /dev/null @@ -1,38 +0,0 @@ -import Downloads - -const PROJECTDIR = dirname(Base.active_project()) -projectdir(names...) = joinpath(PROJECTDIR, names...) -datadir(names...) = projectdir("data", names...) - - -DOWNLOADS = Dict( - "https://github.com/JuliaQuantumControl/Krotov.jl/raw/data-dump/DissGateOCT%23J_T%3DJ_T_re%23iter_stop%3D3000%23method%3Dkrotov.jld2" => - datadir("DissGateOCT#J_T=J_T_re#iter_stop=3000#method=krotov.jld2"), - "https://github.com/JuliaQuantumControl/Krotov.jl/raw/data-dump/parametrization%23opt_result_logisticsq.jld2" => - datadir("parametrization#opt_result_logisticsq.jld2"), - "https://github.com/JuliaQuantumControl/Krotov.jl/raw/data-dump/parametrization%23opt_result_positive.jld2" => - datadir("parametrization#opt_result_positive.jld2"), - "https://github.com/JuliaQuantumControl/Krotov.jl/raw/data-dump/parametrization%23opt_result_tanh.jld2" => - datadir("parametrization#opt_result_tanh.jld2"), - "https://github.com/JuliaQuantumControl/Krotov.jl/raw/data-dump/parametrization%23opt_result_tanhsq.jld2" => - datadir("parametrization#opt_result_tanhsq.jld2"), - "https://github.com/JuliaQuantumControl/Krotov.jl/raw/data-dump/PE_OCT.jld2" => - datadir("PE_OCT.jld2"), - "https://github.com/JuliaQuantumControl/Krotov.jl/raw/data-dump/PE_OCT_direct.jld2" => - datadir("PE_OCT_direct.jld2"), -) - -function download_dump(url, destination; force=false, verbose=true) - if !isfile(destination) || force - mkpath(dirname(destination)) - verbose && (@info "Downloading $url => $destination") - Downloads.download(url, destination) - else - verbose && (@info "$destination OK") - end -end - -@info "Download Dumps" -for (url, destination) in DOWNLOADS - download_dump(url, destination) -end diff --git a/test/examples/.gitignore b/test/examples/.gitignore deleted file mode 100644 index 27d013c..0000000 --- a/test/examples/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.jl diff --git a/test/generate_example_tests.jl b/test/generate_example_tests.jl deleted file mode 100644 index 694566f..0000000 --- a/test/generate_example_tests.jl +++ /dev/null @@ -1,22 +0,0 @@ -import Literate -using Base.Filesystem: cp, basename - -println("Start generating tests for Literate.jl examples") - -EXAMPLEDIR = joinpath(@__DIR__, "..", "examples") -GENERATEDDIR = joinpath(@__DIR__, "examples") -mkpath(GENERATEDDIR) -for name in readdir(EXAMPLEDIR) - path = abspath(joinpath(EXAMPLEDIR, name)) - if endswith(path, ".jl") - example = path - script = Literate.script(example, GENERATEDDIR) - elseif isdir(path) - folder = path - target = joinpath(GENERATEDDIR, basename(folder)) - cp(folder, target, force=true) - @info "cp $folder -> $target" - end -end - -println("Finished generating tests for Literate.jl examples") diff --git a/test/init.jl b/test/init.jl index 429340d..a778d8e 100644 --- a/test/init.jl +++ b/test/init.jl @@ -4,14 +4,10 @@ using Plots unicodeplots() using JuliaFormatter using QuantumControlTestUtils: test, show_coverage, generate_coverage_html -using LiveServer: LiveServer, serve, servedocs as _servedocs +using LiveServer: LiveServer, serve, servedocs using Term include(joinpath(@__DIR__, "clean.jl")) -function servedocs(; kwargs...) - clean() - _servedocs(; skip_dirs=["docs/src/examples"], kwargs...) -end REPL_MESSAGE = """ ******************************************************************************* @@ -21,9 +17,6 @@ Revise, JuliaFormatter, LiveServer, Plots with unicode backend are active. * `help()` – Show this message * `include("test/runtests.jl")` – Run the entire test suite -* `include("test/generate_example_tests.jl")` – Convert all examples to tests -* `include("test/examples/simple_state_to_state.jl")` – - Run an individual example test (after converting examples)! * `test()` – Run the entire test suite in a subprocess with coverage * `show_coverage()` – Print a tabular overview of coverage data * `generate_coverage_html()` – Generate an HTML coverage report diff --git a/test/runtests.jl b/test/runtests.jl index 5e22ac4..1d4c69f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,10 +4,6 @@ using Plots unicodeplots() -include("generate_example_tests.jl") - -include("download_dumps.jl") - # Note: comment outer @testset to stop after first @safetestset failure @time @testset verbose = true "Krotov.jl Package" begin @@ -21,26 +17,5 @@ include("download_dumps.jl") include("test_empty_optimization.jl") end - println("\n* Example 1 (examples/simple_state_to_state.jl):") - @time @safetestset "Example 1 (simple_state_to_state)" begin - include(joinpath("examples", "simple_state_to_state.jl")) - end - - println("\n* Example 2 (examples/rho_3states.jl):") - @time @safetestset "Example 2 (rho_3states)" begin - include(joinpath("examples", "rho_3states.jl")) - end - - println("\n* Example 3 (examples/state_to_state_parametrizations.jl):") - @time @safetestset "Example 3 (state_to_state_parametrization)" begin - include(joinpath("examples", "state_to_state_parametrizations.jl")) - end - - println("\n* Example 4 (examples/perfect_entanglers.jl):") - @time @safetestset "Example 4 (perfect_entanglers)" begin - include(joinpath("examples", "perfect_entanglers.jl")) - end - - println("") - end +nothing