+ Quickly set up and solve problem templates for quantum optimal control
+
+
+
-**QuantumCollocation.jl** uses [NamedTrajectories.jl](https://github.com/kestrelquantum/NamedTrajectories.jl) to set up and solve direct collocation problems specific to quantum optimal control, i.e. generating a *pulse* sequence $a_{1:T-1}$ to drive a quantum system and realize a target gate $U_{\text{goal}}$. We formulate this problem as a nonlinear program (NLP) of the form
+# QuantumCollocation.jl
+**QuantumCollocation.jl** sets up and solves *quantum control problems* as nonlinear programs (NLPs). In this context, a generic quantum control problem looks like
```math
\begin{aligned}
-\underset{U, a, \Delta t}{\text{minimize}} & \quad \ell(U_T, U_{\text{goal}})\\
-\text{ subject to } & \quad U_{t+1} = \exp(-i \Delta t H(a_t)) U_t
+ \arg \min_{\mathbf{Z}}\quad & J(\mathbf{Z}) \\
+ \nonumber \text{s.t.}\qquad & \mathbf{f}(\mathbf{Z}) = 0 \\
+ \nonumber & \mathbf{g}(\mathbf{Z}) \le 0
\end{aligned}
```
+where $\mathbf{Z}$ is a trajectory containing states and controls, from [NamedTrajectories.jl](https://github.com/kestrelquantum/NamedTrajectories.jl).
-Where the dynamics between *knot points* $(U_t, a_t)$ and $(U_{t+1}, a_{t+1})$ are enforced as constraints on the states which are free variables in the solver; this optimization framework is called *direct collocation*. For details of our implementation please see our award-winning IEEE QCE 2023 paper, [Direct Collocation for Quantum Optimal Control](https://arxiv.org/abs/2305.03261). If you use QuantumCollocation.jl in your work, please cite :raised_hands:!
+### Problem Templates
-QuantumCollocation.jl gives the user the ability to add other constraints and objective functions to this problem and solve it efficiently using [Ipopt.jl](https://github.com/jump-dev/Ipopt.jl) and [MathOptInterface.jl](https://github.com/jump-dev/MathOptInterface.jl) under the hood.
+*Problem Templates* are reusable design patterns for setting up and solving common quantum control problems.
-## :warning: Notice :warning:
+For example, a *UnitarySmoothPulseProblem* is tasked with generating a *pulse* sequence $a_{1:T-1}$ in orderd to minimize infidelity, subject to constraints from the Schroedinger equation,
+```math
+ \begin{aligned}
+ \arg \min_{\mathbf{Z}}\quad & |1 - \mathcal{F}(U_T, U_\text{goal})| \\
+ \nonumber \text{s.t.}
+ \qquad & U_{t+1} = \exp\{- i H(a_t) \Delta t_t \} U_t, \quad \forall\, t \\
+ \end{aligned}
+```
+while a *UnitaryMinimumTimeProblem* minimizes time and constrains fidelity,
+```math
+ \begin{aligned}
+ \arg \min_{\mathbf{Z}}\quad & \sum_{t=1}^T \Delta t_t \\
+ \qquad & U_{t+1} = \exp\{- i H(a_t) \Delta t_t \} U_t, \quad \forall\, t \\
+ \nonumber & \mathcal{F}(U_T, U_\text{goal}) \ge 0.9999
+ \end{aligned}
+```
-This package is under active development and issues may arise -- please be patient and report any issues you find!
+In each case, the dynamics between *knot points* $(U_t, a_t)$ and $(U_{t+1}, a_{t+1})$ are enforced as constraints on the states, which are free variables in the solver; this optimization framework is called *direct collocation*. For details of our implementation please see our award-winning IEEE QCE 2023 paper, [Direct Collocation for Quantum Optimal Control](https://arxiv.org/abs/2305.03261). If you use QuantumCollocation.jl in your work, please cite :raised_hands:!
-## Installation
+Problem templates give the user the ability to add other constraints and objective functions to this problem and solve it efficiently using [Ipopt.jl](https://github.com/jump-dev/Ipopt.jl) and [MathOptInterface.jl](https://github.com/jump-dev/MathOptInterface.jl) under the hood.
-QuantumCollocation.jl is registered! To install:
+## Installation
+This package is registered! To install, enter the Julia REPL, type `]` to enter pkg mode, and then run:
```julia
-using Pkg
-Pkg.add(QuantumCollocation)
+pkg> add QuantumCollocation
```
## Example
-### Single Qubit X-Gate
+### Single Qubit Hadamard Gate
```Julia
using QuantumCollocation
T = 50
Δt = 0.2
system = QuantumSystem([PAULIS[:X], PAULIS[:Y]])
+U_goal = GATES.H
# Hadamard Gate
-prob = UnitarySmoothPulseProblem(system, GATES[:H], T, Δt)
+prob = UnitarySmoothPulseProblem(system, U_goal, T, Δt)
solve!(prob, max_iter=100)
-
-plot_unitary_populations(prob)
```
-![Single Qubit X-Gate](docs/src/assets/x_gate_unitary_populations.svg)
-
-## Quickstart developers guide
-
-__Install Julia__ [Juliaup](https://github.com/JuliaLang/juliaup) is an installer and version manager. This is one useful way to manage Julia versions and keep up with the latest changes. After installing, run `julia` to obtain the Julia _REPL_.
-
-__Julia environments__
-[(Documentation)](https://pkgdocs.julialang.org/v1/environments/#Using-someone-else's-project) Your project's environment is stored in _Project.toml_. You can interactively add packages to an environment by using the Julia command line _REPL_ and _package manager_. Start Julia in the project folder. Type `]` to enter the package manager. Type `activate .` to activate or create an environment specified by _Project.toml_ located in the current folder. Separately, you generate a manifest (solving the versions to create a valid environment) by running `instantiate`; instantiate will check that the environment is correct after you add all the packages you want.
-
-__Adding packages__
-[(Documentation)](https://pkgdocs.julialang.org/v1/managing-packages/#Adding-packages) The initial cell for a Piccolo notebook might look something like the following:
-```Julia
-# Standard packages
-using LinearAlgebra
-using CairoMakie
-
-# Piccolo packages
-using QuantumCollocation
-using NamedTrajectories
-using TrajectoryIndexingUtils
-```
-
-First, let's install some standard packages (these are like Numpy and Matplotlib). Open the package manager in the current environment (type `julia`, `]`, and `activate .`), type `add LinearAlgebra` to install and precompile _LinearAlgebra_. Same with `CairoMakie`.
-
-Second, let's install _Piccolo_. There are three packages (_QuantumCollocation_, _NamedTrajetories_, _TrajectoryIndexingUtils_) inside [Piccolo](https://docs.juliahub.com/General/Piccolo/stable/). We could do `add Piccolo` to get the three as a bundle from the Julia repository. Instead of individually calling `using ...` for each, this approach only requires `using Piccolo` at the start of a file or notebook.
-
-As a developer, we want to use the git repositories directly from [the Kestrel Quantum Github page](https://github.com/kestrelquantum). Clone, then add the local packages to the Project file with e.g. `dev ../relative/path/to/repo/QuantumCollocation`. This command installs the development version of _QuantumCollocation_ pointing to the local Github code instead of the package repository. You can repeat this for the others, also.
-
-__Developing__
-[Revise.jl](https://timholy.github.io/Revise.jl/stable/) will let you edit source code, update packages, and reload the changes in a notebook---automatically! This is a great tool for development. `add Revise` from the REPL and then include it before any packages you intend to edit:
-```Julia
-using Revise
-using QuantumCollocation
-```
-
-### Documentation
-
-Documentation is built using [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl) and uses [Literate.jl](https://github.com/fredrikekre/Literate.jl) to generate markdown files from scripts stored in [docs/literate](docs/literate). To build the documentation locally, start julia with the docs environment:
-
-```bash
-julia --project=docs
-```
-
-Then run the following commands in the Julia REPL:
-
-```
-] instantiate
-```
-
-Then (for ease of development) load the following packages:
-
-```julia
-using Revise, LiveServer, QuantumCollocation
-```
-
-To live-serve the docs, run
-```julia
-servedocs(literate_dir="docs/literate", skip_dir="docs/src/generated")
-```
-
-Changes made to files in the docs directory should be automatically reflected in the live server. To reflect changes in the source code (e.g. doc strings), since we are using Revise, simply kill the live server running in the REPL (with, e.g., Ctrl-C) and restart it with the above command.
-
-### Tips for Visual Studio Code
-__Julia extension__ You can run Julia notebooks and much more with [the Julia extension](https://code.visualstudio.com/docs/languages/julia). Upon opening your project folder in VS code and attempting to run an `.ipynb`, you will see that VS Code finds the interpreters managed by juliaup and defaults to using the environment based on the _Project.toml_ in the project directory.
-
-__Fonts__ VS Code will not display all characters allowed by Julia. You can change the editor font family in the settings to `'JuliaMono'` to get full support. If you don't want to mix and mash, you can create a new VS Code settings profile for working in Julia at _File>Preferences>Profile_.
-
-__Tests__ Tests should automatically populate in VS Code when working with a Piccolo package. For example, just by adding the `QuantumCollocation.jl` folder to your workspace, you should see tests appear if you click on the _Testing_ sidebar icon. If you run one of these tests, a new Julia kernel is spawned for the test. You can find the kernel if you click on the _Julia_ sidebar icon (after installing the Julia extensions). Sometimes, for the tests to recognize new changes, you may need to manually kill this kernel to see your changes reflected.
-
diff --git a/assets/logo.svg b/assets/logo.svg
new file mode 100644
index 00000000..9ee565d5
--- /dev/null
+++ b/assets/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/literate/examples/multilevel_transmon.jl b/docs/literate/examples/multilevel_transmon.jl
deleted file mode 100644
index 0281de1f..00000000
--- a/docs/literate/examples/multilevel_transmon.jl
+++ /dev/null
@@ -1,105 +0,0 @@
-# # Multilevel Transmon
-
-# In this example we will look at a multilevel transmon qubit with a Hamiltonian given by
-#
-# ```math
-# \hat{H}(t) = -\frac{\delta}{2} \hat{n}(\hat{n} - 1) + u_1(t) (\hat{a} + \hat{a}^\dagger) + u_2(t) i (\hat{a} - \hat{a}^\dagger)
-# ```
-# where $\hat{n} = \hat{a}^\dagger \hat{a}$ is the number operator, $\hat{a}$ is the annihilation operator, $\delta$ is the anharmonicity, and $u_1(t)$ and $u_2(t)$ are control fields.
-#
-# We will use the following parameter values:
-#
-# ```math
-# \begin{aligned}
-# \delta &= 0.2 \text{ GHz}\\
-# \abs{u_i(t)} &\leq 0.2 \text{ GHz}\\
-# T_0 &= 10 \text{ ns}\\
-# \end{aligned}
-# ```
-#
-# For convenience, we have defined the `TransmonSystem` function in the `QuantumSystemTemplates` module, which returns a `QuantumSystem` object for a transmon qubit. We will use this function to define the system.
-
-# ## Setting up the problem
-
-# To begin, let's load the necessary packages, define the system parameters, and create a a `QuantumSystem` object using the `TransmonSystem` function.
-
-using QuantumCollocation
-using NamedTrajectories
-using LinearAlgebra
-using SparseArrays
-using Random; Random.seed!(123)
-
-## define the time parameters
-
-T₀ = 10 # total time in ns
-T = 50 # number of time steps
-Δt = T₀ / T # time step
-
-## define the system parameters
-levels = 5
-δ = 0.2
-
-## add a bound to the controls
-a_bound = 0.2
-
-## create the system
-sys = TransmonSystem(levels=levels, δ=δ)
-
-## let's look at the parameters of the system
-sys.params
-
-
-# Since this is a multilevel transmon and we want to implement an, let's say, $X$ gate on the qubit subspace, i.e., the first two levels we can utilize the `EmbeddedOperator` type to define the target operator.
-
-## define the target operator
-op = EmbeddedOperator(:X, sys)
-
-## show the full operator
-op.operator |> sparse
-
-# In this formulation, we also use a subspace identity as the initial state, which looks like
-
-get_subspace_identity(op) |> sparse
-
-# We can then pass this embedded operator to the `UnitarySmoothPulseProblem` template to create
-
-## create the problem
-prob = UnitarySmoothPulseProblem(sys, op, T, Δt; a_bound=a_bound)
-
-## solve the problem
-solve!(prob; max_iter=50)
-
-# Let's look at the fidelity in the subspace
-
-println("Fidelity: ", unitary_rollout_fidelity(prob; subspace=op.subspace_indices))
-
-# and plot the result using the `plot_unitary_populations` function.
-
-plot_unitary_populations(prob; fig_size=(900, 700))
-
-# ## Leakage suppresion
-# As can be seen from the above plot, there is a substantial amount of leakage into the higher levels during the evolution. To mitigate this, we have implemented the ability to add a cost to populating the leakage levels, in particular this is an $L_1$ norm cost, which is implemented via slack variables and should ideally drive those leakage populations down to zero.
-# To implement this, pass `leakage_suppresion=true` and `R_leakage={value}` to the `UnitarySmoothPulseProblem` template.
-
-## create the a leakage suppression problem, initializing with the previous solution
-
-prob_leakage = UnitarySmoothPulseProblem(sys, op, T, Δt;
- a_bound=a_bound,
- leakage_suppression=true,
- R_leakage=1e-1,
- a_guess=prob.trajectory.a
-)
-
-## solve the problem
-
-solve!(prob_leakage; max_iter=50)
-
-# Let's look at the fidelity in the subspace
-
-println("Fidelity: ", unitary_rollout_fidelity(prob_leakage; subspace=op.subspace_indices))
-
-# and plot the result using the `plot_unitary_populations` function.
-
-plot_unitary_populations(prob_leakage; fig_size=(900, 700))
-
-# Here we can see that the leakage populations have been driven substantially down.
diff --git a/docs/literate/examples/two_qubit_gates.jl b/docs/literate/examples/two_qubit_gates.jl
deleted file mode 100644
index 78f7de22..00000000
--- a/docs/literate/examples/two_qubit_gates.jl
+++ /dev/null
@@ -1,201 +0,0 @@
-# # Two Qubit Gates
-
-# In this example we will solve for a selection of two-qubit gates using a simple two-qubit system. We will use the [`UnitarySmoothPulseProblem`](@ref) template to solve for the optimal control fields.
-
-# ## Defining our Hamiltonian
-
-# In quantum optimal control we work with Hamiltonians of the form
-
-# ```math
-# H(t) = H_{\text{drift}} + \sum_{j} u^j(t) H_{\text{drive}}^j,
-# ```
-
-# Specifically, for a simple two-qubit system in a rotating frame, we have
-
-# ```math
-# H = J_{12} \sigma_1^x \sigma_2^x + \sum_{i \in {1,2}} a_i^R(t) {\sigma^x_i \over 2} + a_i^I(t) {\sigma^y_i \over 2}.
-# ```
-
-# where
-
-# ```math
-# \begin{align*}
-# J_{12} &= 0.001 \text{ GHz}, \\
-# |a_i^R(t)| &\leq 0.1 \text{ GHz} \\
-# \end{align*}
-# ```
-
-# And the duration of the gate will be capped at $400 \ \mu s$.
-
-# Let's now set this up using some of the convenience functions available in QuantumCollocation.jl.
-
-using QuantumCollocation
-using NamedTrajectories
-using LinearAlgebra
-
-## Define our operators
-σx = GATES[:X]
-σy = GATES[:Y]
-Id = GATES[:I]
-
-## Lift the operators to the two-qubit Hilbert space
-σx_1 = σx ⊗ Id
-σx_2 = Id ⊗ σx
-
-σy_1 = σy ⊗ Id
-σy_2 = Id ⊗ σy
-
-## Define the parameters of the Hamiltonian
-J_12 = 0.001 # GHz
-a_bound = 0.100 # GHz
-
-## Define the drift (coupling) Hamiltonian
-H_drift = J_12 * (σx ⊗ σx)
-
-## Define the control Hamiltonians
-H_drives = [σx_1 / 2, σy_1 / 2, σx_2 / 2, σy_2 / 2]
-
-## Define control (and higher derivative) bounds
-a_bound = 0.1
-da_bound = 0.0005
-dda_bound = 0.0025
-
-## Scale the Hamiltonians by 2π
-H_drift *= 2π
-H_drives .*= 2π
-
-## Define the time parameters
-T = 100 # timesteps
-duration = 100 # μs
-Δt = duration / T
-Δt_max = 400 / T
-
-## Define the system
-sys = QuantumSystem(H_drift, H_drives)
-
-## Look at max eigenvalue of the generator (for deciding if Pade integrators are viable)
-maximum(abs.(eigvals(Δt_max * (H_drift + sum(a_bound .* H_drives)))))
-
-# That this value above is greater than one means that we must use an exponential integrator for these problems. We can set the kwarg `integrator=:exponential` in the [`PiccoloOptions`](@ref) struct as follows.
-
-piccolo_options = PiccoloOptions(
- integrator=:exponential,
-)
-
-# ## SWAP gate
-
-## Define the goal operation
-U_goal = [
- 1 0 0 0;
- 0 0 1 0;
- 0 1 0 0;
- 0 0 0 1
-] |> Matrix{ComplexF64}
-
-## Set up and solve the problem
-
-prob = UnitarySmoothPulseProblem(
- sys,
- U_goal,
- T,
- Δt;
- a_bound=a_bound,
- da_bound=da_bound,
- dda_bound=dda_bound,
- R_da=0.01,
- R_dda=0.01,
- Δt_max=Δt_max,
- piccolo_options=piccolo_options
-)
-
-solve!(prob; max_iter=100)
-
-## Let's take a look at the final fidelity
-unitary_rollout_fidelity(prob)
-
-# Looks good!
-
-# Now let's plot the pulse and the population trajectories for the first two columns of the unitary, i.e. initial state of $\ket{00}$ and $\ket{01}$. For this we provide the function [`plot_unitary_populations`](@ref).
-plot_unitary_populations(prob)
-
-# For fun, let's look at a minimum time pulse for this problem
-
-min_time_prob = UnitaryMinimumTimeProblem(prob; final_fidelity=.99)
-
-solve!(min_time_prob; max_iter=300)
-
-unitary_rollout_fidelity(min_time_prob)
-
-# And let's plot this solution
-plot_unitary_populations(min_time_prob)
-
-# It looks like our pulse derivative bounds are holding back the solution, but regardless, the duration has decreased:
-
-get_duration(prob.trajectory) - get_duration(min_time_prob.trajectory)
-
-
-
-
-# ## Mølmer–Sørensen gate
-
-# Here we will solve for a [Mølmer–Sørensen gate](https://en.wikipedia.org/wiki/M%C3%B8lmer%E2%80%93S%C3%B8rensen_gate) between two. The gate is generally described, for N qubits, by the unitary matrix
-
-# ```math
-# U_{\text{MS}}(\vec\theta) = \exp\left(i\sum_{j=1}^{N-1}\sum_{k=j+1}^{N}\theta_{jk}\sigma_j^x\sigma_k^x\right),
-# ```
-
-# where $\sigma_j^x$ is the Pauli-X operator acting on the $j$-th qubit, and $\vec\theta$ is a vector of real parameters. The Mølmer–Sørensen gate is a two-qubit gate that is particularly well-suited for trapped-ion qubits, where the interaction between qubits is mediated.
-
-# Here we will focus on the simplest case of a Mølmer–Sørensen gate between two qubits. The gate is described by the unitary matrix
-
-# ```math
-# U_{\text{MS}}\left({\pi \over 4}\right) = \exp\left(i\frac{\pi}{4}\sigma_1^x\sigma_2^x\right).
-# ```
-
-# Let's set up the problem.
-
-## Define the goal operation
-U_goal = exp(im * π/4 * σx_1 * σx_2)
-
-## Set up and solve the problem
-
-prob = UnitarySmoothPulseProblem(
- sys,
- U_goal,
- T,
- Δt;
- a_bound=a_bound,
- da_bound=da_bound,
- dda_bound=dda_bound,
- R_da=0.01,
- R_dda=0.01,
- Δt_max=Δt_max,
- piccolo_options=piccolo_options
-)
-
-solve!(prob; max_iter=1_000)
-
-## Let's take a look at the final fidelity
-unitary_rollout_fidelity(prob)
-
-# Again, looks good!
-
-# Now let's plot the pulse and the population trajectories for the first two columns of the unitary, i.e. initial state of $\ket{00}$ and $\ket{01}$.
-
-plot_unitary_populations(prob)
-
-# For fun, let's look at a minimum time pulse for this problem
-
-min_time_prob = UnitaryMinimumTimeProblem(prob; final_fidelity=.999)
-
-solve!(min_time_prob; max_iter=300)
-
-unitary_rollout_fidelity(min_time_prob)
-
-# And let's plot this solution
-
-plot_unitary_populations(min_time_prob)
-
-# It looks like our pulse derivative bounds are holding back the solution, but regardless, the duration has decreased:
-
-get_duration(prob.trajectory) - get_duration(min_time_prob.trajectory)
diff --git a/docs/literate/man/embedded_operators.jl b/docs/literate/man/embedded_operators.jl
deleted file mode 100644
index 9f1c4b5f..00000000
--- a/docs/literate/man/embedded_operators.jl
+++ /dev/null
@@ -1,115 +0,0 @@
-# ```@meta
-# CollapsedDocStrings = true
-# ```
-# # Embedded Operators
-
-# In this manual, we will discuss embedding operators in subspaces of larger quantum systems.
-
-# ## The `embed` and `unembed` functions
-
-# A frequent situation in quantum optimal control is the need to embed a quantum operator in a larger Hilbert space. This is often necessary when the control Hamiltonian acts on a subspace of the full Hilbert space.
-
-# The [`embed`](@ref) function allows to embed a quantum operator in a larger Hilbert space.
-# ```@docs
-# embed
-# ```
-
-# The [`unembed`](@ref) function allows to unembed a quantum operator from a larger Hilbert space.
-# ```@docs
-# unembed
-# ```
-
-# For example, for a single qubit X gate embedded in a multilevel system:
-
-using QuantumCollocation
-using SparseArrays # for visualization
-
-## define levels of full system
-levels = 3
-
-## get a 2-level X gate
-X = GATES[:X]
-
-## define subspace indices as lowest two levels
-subspace_indices = 1:2
-
-## embed the X gate in the full system
-X_embedded = embed(X, subspace_indices, levels)
-
-# We can retrieve the original operator:
-
-X_unembedded = unembed(X_embedded, subspace_indices)
-
-
-# ## The `EmbeddedOperator` type
-
-# The `EmbeddedOperator` type is a convenient way to define an operator embedded in a subspace of a larger quantum system.
-
-# The `EmbeddedOperator` type is defined as follows:
-
-# ```@docs
-# EmbeddedOperator
-# ```
-
-# And can be constructed using the following method:
-
-# ```@docs
-# EmbeddedOperator(op::Matrix{<:Number}, subspace_indices::AbstractVector{Int}, subsystem_levels::AbstractVector{Int})
-# ```
-
-# For example, for an X gate on the first qubit of two qubit, 3-level system:
-
-## define the target operator X ⊗ I
-gate = GATES[:X] ⊗ GATES[:I]
-
-## define the subsystem levels
-subsystem_levels = [3, 3]
-
-## define the subspace indices
-subspace_indices = get_subspace_indices([1:2, 1:2], subsystem_levels)
-
-## create the embedded operator
-op = EmbeddedOperator(gate, subspace_indices, subsystem_levels)
-
-## show the full operator
-op.operator .|> abs |> sparse
-
-# We can get the original operator back:
-
-gate_unembeded = unembed(op)
-
-gate_unembeded .|> abs |> sparse
-
-# ## The `get_subspace_indices` function
-
-# The `get_subspace_indices` function is a convenient way to get the indices of a subspace in a larger quantum system.
-
-# ### Simple quantum systems
-# For simple (non-composite) quantum systems, such as a single multilevel qubit, we provode the following method:
-
-# ```@docs
-# get_subspace_indices(subspace::AbstractVector{Int}, levels::Int)
-# ```
-
-## get the indices of the lowest two levels of a 3-level system
-subspace_indices = get_subspace_indices(1:2, 3)
-
-# ### Comosite quantum systems
-# For composite quantum systems, such as a two qubit system, we provide the following methods.
-
-# Targeting subspaces in a composite quantum system, with general subsystem levels:
-# ```@docs
-# get_subspace_indices(subspaces::Vector{<:AbstractVector{Int}}, subsystem_levels::AbstractVector{Int})
-# ```
-
-## get the subspace indices for a three level qubit coupled to a 9-level cavity
-get_subspace_indices([1:2, 1:2], [3, 9])
-
-
-# Targeting subspaces in a composite quantum system, with all subsystems having the same number of levels:
-# ```@docs
-# get_subspace_indices(levels::AbstractVector{Int}; subspace=1:2, kwargs...)
-# ```
-
-## get the subspace indices for a two qubit system with 3 levels each
-get_subspace_indices([3, 3])
diff --git a/docs/literate/man/problem_templates.jl b/docs/literate/man/problem_templates.jl
index ba58fd2c..a5d12f9d 100644
--- a/docs/literate/man/problem_templates.jl
+++ b/docs/literate/man/problem_templates.jl
@@ -1,15 +1,19 @@
+# ```@meta
+# CollapsedDocStrings = true
+# ```
+
# # Problem Templates
# We provide a number of problem templates for making it simple and easy to set up and solve certain types of quantum optimal control problems. These templates all construct a `QuantumControlProblem` object. The problem templates are:
# ## Unitary Smooth Pulse Problem
-# ```@docs
+# ```@docs; canonical = false
# UnitarySmoothPulseProblem
# ```
# ## Unitary Minimum Time Problem
-# ```@docs
+# ```@docs; canonical = false
# UnitaryMinimumTimeProblem
# ```
diff --git a/docs/literate/man/rollouts.jl b/docs/literate/man/rollouts.jl
new file mode 100644
index 00000000..87aacad6
--- /dev/null
+++ b/docs/literate/man/rollouts.jl
@@ -0,0 +1,16 @@
+# ```@meta
+# CollapsedDocStrings = true
+# ```
+
+# # Rollouts
+
+using QuantumCollocation
+using SparseArrays # for visualization
+
+#=
+
+Rollouts are a way to visualize the evolution of a quantum system. The various rollout
+functions provided in this module allow for the validation of the solution to a quantum
+optimal control problem.
+
+=#
\ No newline at end of file
diff --git a/docs/literate/quickstart.jl b/docs/literate/quickstart.jl
deleted file mode 100644
index 715ab0ea..00000000
--- a/docs/literate/quickstart.jl
+++ /dev/null
@@ -1,102 +0,0 @@
-# # Quickstart Guide
-
-# To set up and solve a quantum optimal control problems we provide high level problem templates to quickly get started. For unitary gate problems, where we want to realize a gate $U_{\text{goal}}$, with a system Hamiltonian of the form,
-# ```math
-# H(t) = H_0 + \sum_i a^i(t) H_i
-# ```
-# there is the `UnitarySmoothPulseProblem` constructor which only requires
-# - the drift Hamiltonian, $H_0$
-# - the drive Hamiltonians, $\qty{H_i}$
-# - the target unitary, $U_{\text{goal}}$
-# - the number of timesteps, $T$
-# - the (initial) time step size, $\Delta t$
-
-# ## Basic Usage
-
-# For example, to create a problem for a single qubit $X$ gate (with a bound on the drive of $|a^i| < a_{\text{bound}}$), i.e., with system hamiltonian
-# ```math
-# H(t) = \frac{\omega}{2} \sigma_z + a^1(t) \sigma_x + a^2(t) \sigma_y
-# ```
-# we can do the following:
-
-using NamedTrajectories
-using QuantumCollocation
-
-## set time parameters
-T = 100
-Δt = 0.1
-
-## use the exported gate dictionary to get the gates we need
-σx = GATES[:X]
-σy = GATES[:Y]
-σz = GATES[:Z]
-
-## define drift and drive Hamiltonians
-H_drift = 0.5 * σz
-H_drives = [σx, σy]
-
-## define target unitary
-U_goal = σx
-
-## set bound on the drive
-a_bound = 1.0
-
-## build the problem
-prob = UnitarySmoothPulseProblem(
- H_drift,
- H_drives,
- U_goal,
- T,
- Δt;
- a_bound=a_bound,
-)
-
-## solve the problem
-solve!(prob; max_iter=30)
-
-# The above output comes from the Ipopt.jl solver. To see the final fidelity we can use the `unitary_fidelity` function exported by QuantumCollocation.jl.
-
-println("Final fidelity: ", unitary_rollout_fidelity(prob))
-
-# We can also easily plot the solutions using the `plot` function exported by NamedTrajectories.jl.
-
-plot(prob.trajectory, [:Ũ⃗, :a])
-
-# ## Minimum Time Problems
-
-# We can also easily set up and solve a minimum time problem, where we enforce a constraint on the final fidelity:
-# ```math
-# \mathcal{F}(U_T, U_{\text{goal}}) \geq \mathcal{F}_{\text{min}}
-# ```
-# Using the problem we just solved we can do the following:
-
-## final fidelity constraint
-final_fidelity = 0.99
-
-## weight on the minimum time objective
-D = 10.0
-
-prob_min_time = UnitaryMinimumTimeProblem(
- prob;
- final_fidelity=final_fidelity,
- D=D
-)
-
-solve!(prob_min_time; max_iter=30)
-
-# We can see that the final fidelity is indeed greater than the minimum fidelity we set.
-
-println("Final fidelity: ", unitary_rollout_fidelity(prob_min_time))
-
-# and that the duration of the pulse has decreased.
-
-initial_dur = get_times(prob.trajectory)[end]
-min_time_dur = get_times(prob_min_time.trajectory)[end]
-
-println("Initial duration: ", initial_dur)
-println("Minimum duration: ", min_time_dur)
-println("Duration decrease: ", initial_dur - min_time_dur)
-
-# We can also plot the solutions for the minimum time problem.
-
-plot(prob_min_time.trajectory, [:Ũ⃗, :a])
diff --git a/docs/make.jl b/docs/make.jl
index eabb4efd..5c0612b4 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -4,23 +4,26 @@ using Literate
push!(LOAD_PATH, joinpath(@__DIR__, "..", "src"))
-# DocMeta.setdocmeta!(QuantumCollocation, :DocTestSetup, :(using QuantumCollocation); recursive=true)
+@info "Building Documenter site for NamedTrajectories.jl"
+open(joinpath(@__DIR__, "src", "index.md"), write = true) do io
+ for line in eachline(joinpath(@__DIR__, "..", "README.md"))
+ if occursin("", line)
+ comment_content = match(r"", line).captures[1]
+ write(io, comment_content * "\n")
+ else
+ write(io, line * "\n")
+ end
+ end
+end
pages = [
"Home" => "index.md",
- "Quickstart Guide" => "generated/quickstart.md",
- "Contribution Guide" => "contribution_guide.md",
"Manual" => [
"Problem Templates" => "generated/man/problem_templates.md",
- "Embedded Operators" => "generated/man/embedded_operators.md",
+ "Rollouts" => "generated/man/rollouts.md",
"Callbacks" => "generated/man/ipopt_callbacks.md",
],
- "Examples" => [
- "Two Qubit Gates" => "generated/examples/two_qubit_gates.md",
- "Multilevel Transmon" => "generated/examples/multilevel_transmon.md",
- ],
"Library" => "lib.md",
- "Release Notes" => "release_notes.md",
]
format = Documenter.HTML(;
diff --git a/docs/src/assets/animation.gif b/docs/src/assets/animation.gif
deleted file mode 100644
index 2a79f44d..00000000
Binary files a/docs/src/assets/animation.gif and /dev/null differ
diff --git a/docs/src/assets/diagram.svg b/docs/src/assets/diagram.svg
deleted file mode 100644
index e1b91d13..00000000
--- a/docs/src/assets/diagram.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/assets/min_time_smooth_pulse_animation.gif b/docs/src/assets/min_time_smooth_pulse_animation.gif
deleted file mode 100644
index 97eb0654..00000000
Binary files a/docs/src/assets/min_time_smooth_pulse_animation.gif and /dev/null differ
diff --git a/docs/src/assets/pulse-optimization.gif b/docs/src/assets/pulse-optimization.gif
deleted file mode 100644
index 69e1adf7..00000000
Binary files a/docs/src/assets/pulse-optimization.gif and /dev/null differ
diff --git a/docs/src/assets/smooth_pulse_animation.gif b/docs/src/assets/smooth_pulse_animation.gif
deleted file mode 100644
index d955946b..00000000
Binary files a/docs/src/assets/smooth_pulse_animation.gif and /dev/null differ
diff --git a/docs/src/assets/x_gate_unitary_populations.svg b/docs/src/assets/x_gate_unitary_populations.svg
deleted file mode 100644
index 19e9262a..00000000
--- a/docs/src/assets/x_gate_unitary_populations.svg
+++ /dev/null
@@ -1,909 +0,0 @@
-
-
diff --git a/docs/src/contribution_guide.md b/docs/src/contribution_guide.md
deleted file mode 100644
index 2341baff..00000000
--- a/docs/src/contribution_guide.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# Contribution Guide
-
-## Introduction
-
-We welcome contributiuons to QuantumCollocation.jl! This document outlines the guidelines for contributing to the project. If you know what you want to see, but are unsure of the best way to achieve it, [add an issue](https://github.com/aarontrowbridge/QuantumCollocation.jl/issues) and start a discussion with the community!
-
-*Let us know how you are using Piccolo!* We enjoy hearing about the problems our users are solving.
-
-## Developing
-
-We recommend creating a fresh environment, and using the `dev` command to install `QuantumCollocation.jl` from source. Adding [`Revise.jl`](https://github.com/timholy/Revise.jl) to this environment allows us to make changes to `QuantumCollocation.jl` during development, without restarting Julia or notebooks in VSCode.
-
-Here are a few places to think about participating!
-
-**Documentation:**
-- [ ] cross-referencing to library
-- [ ] adding docstrings, examples, and tests of utilities
-- [ ] examples
- - [ ] two-qubit
- - [ ] cat qubit
- - [ ] three-qubit
- - [ ] qubit-cavity
- - [ ] qubit-cavity-qubit
-- [ ] document type requirements for `Constraints`, `Losses`, and `Objectives`
-
-
-**Functionality:**
-- [ ] custom `QuantumTrajectory` types (repr. of isomorphic states)
-- [ ] better quantum system constructors (e.g. storing composite system info)
-- [ ] refactor `Objectives` and distinguish from `Losses`
-
-### Documentation
-
-Documentation is built using [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl) and uses [Literate.jl](https://github.com/fredrikekre/Literate.jl) to generate markdown files from scripts stored in [docs/literate](docs/literate). To build the documentation locally, start julia with the docs environment:
-
-```bash
-julia --project=docs
-```
-
-Then (for ease of development) load the following packages:
-
-```julia
-using Revise, LiveServer, QuantumCollocation
-```
-
-To live-serve the docs, run
-```julia
-servedocs(literate_dir="docs/literate", skip_dir="docs/src/generated")
-```
-
-Changes made to files in the docs directory should be automatically reflected in the live server. To reflect changes in the source code (e.g. doc strings), since we are using Revise, simply kill the live server running in the REPL (with, e.g., Ctrl-C) and restart it with the above command.
-
-### Writing tests
-
-Tests are implemented using the [`TestItemRunner.jl`](https://github.com/julia-vscode/TestItemRunner.jl) package.
-
-```Julia
-@testitem "Hadamard gate" begin
- H_drift, H_drives = GATES[:Z], [GATES[:X], GATES[:Y]]
- U_goal = GATES[:H]
- T = 51
- Δt = 0.2
-
- prob = UnitarySmoothPulseProblem(
- H_drift, H_drives, U_goal, T, Δt,
- ipopt_options=IpoptOptions(print_level=1)
- )
-
- solve!(prob, max_iter=100)
- @test unitary_fidelity(prob) > 0.99
-end
-```
-
-Individual tests will populate in the Testing panel in VSCode. All tests are integrated into the base test system for CI, which occurs at each PR submission.
-
-We organize our tests in two ways:
-1. Modules in single files (e.g. `quantum_utils.jl`, `direct_sums.jl`) should have a single test file in the `test/` directory.
-2. Module directories containing templates (e.g. `quantum_system_templates/`, `problem_templates/`) should include tests in the same file that the template is defined, so `problem_templates/unitary_smooth_pulse_problem.jl` includes the test items for `UnitarySmoothPulseProblem`.
-
-### Reporting Issues
-
-Issue templates are available on GitHub. We are happy to take feature requests!
\ No newline at end of file
diff --git a/docs/src/generated/examples/multilevel_transmon.md b/docs/src/generated/examples/multilevel_transmon.md
deleted file mode 100644
index 2b011a3a..00000000
--- a/docs/src/generated/examples/multilevel_transmon.md
+++ /dev/null
@@ -1,131 +0,0 @@
-```@meta
-EditURL = "../../../literate/examples/multilevel_transmon.jl"
-```
-
-# Multilevel Transmon
-
-In this example we will look at a multilevel transmon qubit with a Hamiltonian given by
-
-```math
-\hat{H}(t) = -\frac{\delta}{2} \hat{n}(\hat{n} - 1) + u_1(t) (\hat{a} + \hat{a}^\dagger) + u_2(t) i (\hat{a} - \hat{a}^\dagger)
-```
-where $\hat{n} = \hat{a}^\dagger \hat{a}$ is the number operator, $\hat{a}$ is the annihilation operator, $\delta$ is the anharmonicity, and $u_1(t)$ and $u_2(t)$ are control fields.
-
-We will use the following parameter values:
-
-```math
-\begin{aligned}
-\delta &= 0.2 \text{ GHz}\\
-\abs{u_i(t)} &\leq 0.2 \text{ GHz}\\
-T_0 &= 10 \text{ ns}\\
-\end{aligned}
-```
-
-For convenience, we have defined the `TransmonSystem` function in the `QuantumSystemTemplates` module, which returns a `QuantumSystem` object for a transmon qubit. We will use this function to define the system.
-
-## Setting up the problem
-
-To begin, let's load the necessary packages, define the system parameters, and create a a `QuantumSystem` object using the `TransmonSystem` function.
-
-````@example multilevel_transmon
-using QuantumCollocation
-using NamedTrajectories
-using LinearAlgebra
-using SparseArrays
-using Random; Random.seed!(123)
-
-# define the time parameters
-
-T₀ = 10 # total time in ns
-T = 50 # number of time steps
-Δt = T₀ / T # time step
-
-# define the system parameters
-levels = 5
-δ = 0.2
-
-# add a bound to the controls
-a_bound = 0.2
-
-# create the system
-sys = TransmonSystem(levels=levels, δ=δ)
-
-# let's look at the parameters of the system
-sys.params
-````
-
-Since this is a multilevel transmon and we want to implement an, let's say, $X$ gate on the qubit subspace, i.e., the first two levels we can utilize the `EmbeddedOperator` type to define the target operator.
-
-````@example multilevel_transmon
-# define the target operator
-op = EmbeddedOperator(:X, sys)
-
-# show the full operator
-op.operator |> sparse
-````
-
-In this formulation, we also use a subspace identity as the initial state, which looks like
-
-````@example multilevel_transmon
-get_subspace_identity(op) |> sparse
-````
-
-We can then pass this embedded operator to the `UnitarySmoothPulseProblem` template to create
-
-````@example multilevel_transmon
-# create the problem
-prob = UnitarySmoothPulseProblem(sys, op, T, Δt; a_bound=a_bound)
-
-# solve the problem
-solve!(prob; max_iter=50)
-````
-
-Let's look at the fidelity in the subspace
-
-````@example multilevel_transmon
-println("Fidelity: ", unitary_rollout_fidelity(prob; subspace=op.subspace_indices))
-````
-
-and plot the result using the `plot_unitary_populations` function.
-
-````@example multilevel_transmon
-plot_unitary_populations(prob; fig_size=(900, 700))
-````
-
-## Leakage suppresion
-As can be seen from the above plot, there is a substantial amount of leakage into the higher levels during the evolution. To mitigate this, we have implemented the ability to add a cost to populating the leakage levels, in particular this is an $L_1$ norm cost, which is implemented via slack variables and should ideally drive those leakage populations down to zero.
-To implement this, pass `leakage_suppresion=true` and `R_leakage={value}` to the `UnitarySmoothPulseProblem` template.
-
-````@example multilevel_transmon
-# create the a leakage suppression problem, initializing with the previous solution
-
-prob_leakage = UnitarySmoothPulseProblem(sys, op, T, Δt;
- a_bound=a_bound,
- leakage_suppression=true,
- R_leakage=1e-1,
- a_guess=prob.trajectory.a
-)
-
-# solve the problem
-
-solve!(prob_leakage; max_iter=50)
-````
-
-Let's look at the fidelity in the subspace
-
-````@example multilevel_transmon
-println("Fidelity: ", unitary_rollout_fidelity(prob_leakage; subspace=op.subspace_indices))
-````
-
-and plot the result using the `plot_unitary_populations` function.
-
-````@example multilevel_transmon
-plot_unitary_populations(prob_leakage; fig_size=(900, 700))
-````
-
-Here we can see that the leakage populations have been driven substantially down.
-
----
-
-*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*
-
diff --git a/docs/src/generated/examples/two_qubit_gates.md b/docs/src/generated/examples/two_qubit_gates.md
deleted file mode 100644
index fedba3bf..00000000
--- a/docs/src/generated/examples/two_qubit_gates.md
+++ /dev/null
@@ -1,233 +0,0 @@
-```@meta
-EditURL = "../../../literate/examples/two_qubit_gates.jl"
-```
-
-# Two Qubit Gates
-
-In this example we will solve for a selection of two-qubit gates using a simple two-qubit system. We will use the [`UnitarySmoothPulseProblem`](@ref) template to solve for the optimal control fields.
-
-## Defining our Hamiltonian
-
-In quantum optimal control we work with Hamiltonians of the form
-
-```math
-H(t) = H_{\text{drift}} + \sum_{j} u^j(t) H_{\text{drive}}^j,
-```
-
-Specifically, for a simple two-qubit system in a rotating frame, we have
-
-```math
-H = J_{12} \sigma_1^x \sigma_2^x + \sum_{i \in {1,2}} a_i^R(t) {\sigma^x_i \over 2} + a_i^I(t) {\sigma^y_i \over 2}.
-```
-
-where
-
-```math
-\begin{align*}
-J_{12} &= 0.001 \text{ GHz}, \\
-|a_i^R(t)| &\leq 0.1 \text{ GHz} \\
-\end{align*}
-```
-
-And the duration of the gate will be capped at $400 \ \mu s$.
-
-Let's now set this up using some of the convenience functions available in QuantumCollocation.jl.
-
-````@example two_qubit_gates
-using QuantumCollocation
-using NamedTrajectories
-using LinearAlgebra
-
-# Define our operators
-σx = GATES[:X]
-σy = GATES[:Y]
-Id = GATES[:I]
-
-# Lift the operators to the two-qubit Hilbert space
-σx_1 = σx ⊗ Id
-σx_2 = Id ⊗ σx
-
-σy_1 = σy ⊗ Id
-σy_2 = Id ⊗ σy
-
-# Define the parameters of the Hamiltonian
-J_12 = 0.001 # GHz
-a_bound = 0.100 # GHz
-
-# Define the drift (coupling) Hamiltonian
-H_drift = J_12 * (σx ⊗ σx)
-
-# Define the control Hamiltonians
-H_drives = [σx_1 / 2, σy_1 / 2, σx_2 / 2, σy_2 / 2]
-
-# Define control (and higher derivative) bounds
-a_bound = 0.1
-da_bound = 0.0005
-dda_bound = 0.0025
-
-# Scale the Hamiltonians by 2π
-H_drift *= 2π
-H_drives .*= 2π
-
-# Define the time parameters
-T = 100 # timesteps
-duration = 100 # μs
-Δt = duration / T
-Δt_max = 400 / T
-
-# Define the system
-sys = QuantumSystem(H_drift, H_drives)
-
-# Look at max eigenvalue of the generator (for deciding if Pade integrators are viable)
-maximum(abs.(eigvals(Δt_max * (H_drift + sum(a_bound .* H_drives)))))
-````
-
-That this value above is greater than one means that we must use an exponential integrator for these problems. We can set the kwarg `integrator=:exponential` in the [`PiccoloOptions`](@ref) struct as follows.
-
-````@example two_qubit_gates
-piccolo_options = PiccoloOptions(
- integrator=:exponential,
-)
-````
-
-## SWAP gate
-
-````@example two_qubit_gates
-# Define the goal operation
-U_goal = [
- 1 0 0 0;
- 0 0 1 0;
- 0 1 0 0;
- 0 0 0 1
-] |> Matrix{ComplexF64}
-
-# Set up and solve the problem
-
-prob = UnitarySmoothPulseProblem(
- sys,
- U_goal,
- T,
- Δt;
- a_bound=a_bound,
- da_bound=da_bound,
- dda_bound=dda_bound,
- R_da=0.01,
- R_dda=0.01,
- Δt_max=Δt_max,
- piccolo_options=piccolo_options
-)
-
-solve!(prob; max_iter=100)
-
-# Let's take a look at the final fidelity
-unitary_rollout_fidelity(prob)
-````
-
-Looks good!
-
-Now let's plot the pulse and the population trajectories for the first two columns of the unitary, i.e. initial state of $\ket{00}$ and $\ket{01}$. For this we provide the function [`plot_unitary_populations`](@ref).
-
-````@example two_qubit_gates
-plot_unitary_populations(prob)
-````
-
-For fun, let's look at a minimum time pulse for this problem
-
-````@example two_qubit_gates
-min_time_prob = UnitaryMinimumTimeProblem(prob; final_fidelity=.99)
-
-solve!(min_time_prob; max_iter=300)
-
-unitary_rollout_fidelity(min_time_prob)
-````
-
-And let's plot this solution
-
-````@example two_qubit_gates
-plot_unitary_populations(min_time_prob)
-````
-
-It looks like our pulse derivative bounds are holding back the solution, but regardless, the duration has decreased:
-
-````@example two_qubit_gates
-get_duration(prob.trajectory) - get_duration(min_time_prob.trajectory)
-````
-
-## Mølmer–Sørensen gate
-
-Here we will solve for a [Mølmer–Sørensen gate](https://en.wikipedia.org/wiki/M%C3%B8lmer%E2%80%93S%C3%B8rensen_gate) between two. The gate is generally described, for N qubits, by the unitary matrix
-
-```math
-U_{\text{MS}}(\vec\theta) = \exp\left(i\sum_{j=1}^{N-1}\sum_{k=j+1}^{N}\theta_{jk}\sigma_j^x\sigma_k^x\right),
-```
-
-where $\sigma_j^x$ is the Pauli-X operator acting on the $j$-th qubit, and $\vec\theta$ is a vector of real parameters. The Mølmer–Sørensen gate is a two-qubit gate that is particularly well-suited for trapped-ion qubits, where the interaction between qubits is mediated.
-
-Here we will focus on the simplest case of a Mølmer–Sørensen gate between two qubits. The gate is described by the unitary matrix
-
-```math
-U_{\text{MS}}\left({\pi \over 4}\right) = \exp\left(i\frac{\pi}{4}\sigma_1^x\sigma_2^x\right).
-```
-
-Let's set up the problem.
-
-````@example two_qubit_gates
-# Define the goal operation
-U_goal = exp(im * π/4 * σx_1 * σx_2)
-
-# Set up and solve the problem
-
-prob = UnitarySmoothPulseProblem(
- sys,
- U_goal,
- T,
- Δt;
- a_bound=a_bound,
- da_bound=da_bound,
- dda_bound=dda_bound,
- R_da=0.01,
- R_dda=0.01,
- Δt_max=Δt_max,
- piccolo_options=piccolo_options
-)
-
-solve!(prob; max_iter=1_000)
-
-# Let's take a look at the final fidelity
-unitary_rollout_fidelity(prob)
-````
-
-Again, looks good!
-
-Now let's plot the pulse and the population trajectories for the first two columns of the unitary, i.e. initial state of $\ket{00}$ and $\ket{01}$.
-
-````@example two_qubit_gates
-plot_unitary_populations(prob)
-````
-
-For fun, let's look at a minimum time pulse for this problem
-
-````@example two_qubit_gates
-min_time_prob = UnitaryMinimumTimeProblem(prob; final_fidelity=.999)
-
-solve!(min_time_prob; max_iter=300)
-
-unitary_rollout_fidelity(min_time_prob)
-````
-
-And let's plot this solution
-
-````@example two_qubit_gates
-plot_unitary_populations(min_time_prob)
-````
-
-It looks like our pulse derivative bounds are holding back the solution, but regardless, the duration has decreased:
-
-````@example two_qubit_gates
-get_duration(prob.trajectory) - get_duration(min_time_prob.trajectory)
-````
-
----
-
-*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*
-
diff --git a/docs/src/generated/man/embedded_operators.md b/docs/src/generated/man/embedded_operators.md
deleted file mode 100644
index 3621bfdc..00000000
--- a/docs/src/generated/man/embedded_operators.md
+++ /dev/null
@@ -1,136 +0,0 @@
-```@meta
-EditURL = "../../../literate/man/embedded_operators.jl"
-```
-
-```@meta
-CollapsedDocStrings = true
-```
-# Embedded Operators
-
-In this manual, we will discuss embedding operators in subspaces of larger quantum systems.
-
-## The `embed` and `unembed` functions
-
-A frequent situation in quantum optimal control is the need to embed a quantum operator in a larger Hilbert space. This is often necessary when the control Hamiltonian acts on a subspace of the full Hilbert space.
-
-The [`embed`](@ref) function allows to embed a quantum operator in a larger Hilbert space.
-```@docs
-embed
-```
-
-The [`unembed`](@ref) function allows to unembed a quantum operator from a larger Hilbert space.
-```@docs
-unembed
-```
-
-For example, for a single qubit X gate embedded in a multilevel system:
-
-````@example embedded_operators
-using QuantumCollocation
-using SparseArrays # for visualization
-
-# define levels of full system
-levels = 3
-
-# get a 2-level X gate
-X = GATES[:X]
-
-# define subspace indices as lowest two levels
-subspace_indices = 1:2
-
-# embed the X gate in the full system
-X_embedded = embed(X, subspace_indices, levels)
-````
-
-We can retrieve the original operator:
-
-````@example embedded_operators
-X_unembedded = unembed(X_embedded, subspace_indices)
-````
-
-## The `EmbeddedOperator` type
-
-The `EmbeddedOperator` type is a convenient way to define an operator embedded in a subspace of a larger quantum system.
-
-The `EmbeddedOperator` type is defined as follows:
-
-```@docs
-EmbeddedOperator
-```
-
-And can be constructed using the following method:
-
-```@docs
-EmbeddedOperator(op::Matrix{<:Number}, subspace_indices::AbstractVector{Int}, subsystem_levels::AbstractVector{Int})
-```
-
-For example, for an X gate on the first qubit of two qubit, 3-level system:
-
-````@example embedded_operators
-# define the target operator X ⊗ I
-gate = GATES[:X] ⊗ GATES[:I]
-
-# define the subsystem levels
-subsystem_levels = [3, 3]
-
-# define the subspace indices
-subspace_indices = get_subspace_indices([1:2, 1:2], subsystem_levels)
-
-# create the embedded operator
-op = EmbeddedOperator(gate, subspace_indices, subsystem_levels)
-
-# show the full operator
-op.operator .|> abs |> sparse
-````
-
-We can get the original operator back:
-
-````@example embedded_operators
-gate_unembeded = unembed(op)
-
-gate_unembeded .|> abs |> sparse
-````
-
-## The `get_subspace_indices` function
-
-The `get_subspace_indices` function is a convenient way to get the indices of a subspace in a larger quantum system.
-
-### Simple quantum systems
-For simple (non-composite) quantum systems, such as a single multilevel qubit, we provode the following method:
-
-```@docs
-get_subspace_indices(subspace::AbstractVector{Int}, levels::Int)
-```
-
-````@example embedded_operators
-# get the indices of the lowest two levels of a 3-level system
-subspace_indices = get_subspace_indices(1:2, 3)
-````
-
-### Comosite quantum systems
-For composite quantum systems, such as a two qubit system, we provide the following methods.
-
-Targeting subspaces in a composite quantum system, with general subsystem levels:
-```@docs
-get_subspace_indices(subspaces::Vector{<:AbstractVector{Int}}, subsystem_levels::AbstractVector{Int})
-```
-
-````@example embedded_operators
-# get the subspace indices for a three level qubit coupled to a 9-level cavity
-get_subspace_indices([1:2, 1:2], [3, 9])
-````
-
-Targeting subspaces in a composite quantum system, with all subsystems having the same number of levels:
-```@docs
-get_subspace_indices(levels::AbstractVector{Int}; subspace=1:2, kwargs...)
-```
-
-````@example embedded_operators
-# get the subspace indices for a two qubit system with 3 levels each
-get_subspace_indices([3, 3])
-````
-
----
-
-*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*
-
diff --git a/docs/src/generated/man/ipopt_callbacks.md b/docs/src/generated/man/ipopt_callbacks.md
deleted file mode 100644
index 04815ca3..00000000
--- a/docs/src/generated/man/ipopt_callbacks.md
+++ /dev/null
@@ -1,126 +0,0 @@
-```@meta
-EditURL = "../../../literate/man/ipopt_callbacks.jl"
-```
-
-```@meta
-CollapsedDocStrings = true
-```
-# IpOpt Callbacks
-
-This page describes the callback functions that can be used with the IpOpt solver (in the future, may describe more general callback behavior).
-
-## Callbacks
-
-````@example ipopt_callbacks
-using QuantumCollocation
-using NamedTrajectories
-
-import ..QuantumStateSmoothPulseProblem
-import ..Callbacks
-````
-
-By default, IpOpt callbacks are called at each optimization step with the following signature:
-
-````@example ipopt_callbacks
-function full_argument_list_callback(
- alg_mod::Cint,
- iter_count::Cint,
- obj_value::Float64,
- inf_pr::Float64,
- inf_du::Float64,
- mu::Float64,
- d_norm::Float64,
- regularization_size::Float64,
- alpha_du::Float64,
- alpha_pr::Float64,
- ls_trials::Cint,
-)
- return true
-end
-````
-
-This gives the user access to some of the optimization state internals at each iteration.
-A callback function with any subset of these arguments can be passed into the `solve!` function via the `callback` keyword argument see below.
-
-The callback function can be used to stop the optimization early by returning `false`. The following callback when passed to `solve!` will stop the optimization after the first iteration:
-
-````@example ipopt_callbacks
-my_callback = (kwargs...) -> false
-````
-
-Single initial and target states
---------------------------------
-
-````@example ipopt_callbacks
-T = 50
-Δt = 0.2
-sys = QuantumSystem(0.1 * GATES[:Z], [GATES[:X], GATES[:Y]])
-ψ_init = Vector{ComplexF64}([1.0, 0.0])
-ψ_target = Vector{ComplexF64}([0.0, 1.0])
-
-prob = QuantumStateSmoothPulseProblem(
- sys, ψ_init, ψ_target, T, Δt;
- ipopt_options=IpoptOptions(print_level=1),
- piccolo_options=PiccoloOptions(verbose=false)
-)
-````
-
-The callback function can be used to monitor the optimization progress, save intermediate results, or modify the optimization process.
-For example, the following callback function saves the optimization trajectory at each iteration - this can be useful for debugging or plotting the optimization progress.
-`trajectory_history_callback` from the `Callbacks` module
-
-````@example ipopt_callbacks
-callback, trajectory_history = QuantumCollocation.Callbacks.trajectory_history_callback(prob)
-solve!(prob, max_iter=20, callback=callback)
-````
-
-Save trajectory images into files which can be used to create a gif like the following:
-
-````@example ipopt_callbacks
-for (iter, traj) in enumerate(trajectory_history)
- str_index = lpad(iter, length(string(length(trajectory_history))), "0")
- plot("./iteration-$str_index-trajectory.png", traj, [:ψ̃, :a], xlims=(-Δt, (T+5)*Δt), ylims=(ψ̃1 = (-2, 2), a = (-1.1, 1.1)))
-end
-````
-
-![pulse optimization animation](../../assets/animation.gif)
-
-Using a callback to get the best trajectory from all the optimization iterations
-
-````@example ipopt_callbacks
-sys2 = QuantumSystem(0.15 * GATES[:Z], [GATES[:X], GATES[:Y]])
-ψ_init2 = Vector{ComplexF64}([0.0, 1.0])
-ψ_target2 = Vector{ComplexF64}([1.0, 0.0])
-````
-
-Using other callbacks from the callback library
---------------------------------
-Callback used here is `best_rollout_fidelity_callback` which appends the best trajectories based on monotonically increasing fidelity of the rollout
-
-````@example ipopt_callbacks
-prob2 = QuantumStateSmoothPulseProblem(
- sys2, ψ_init2, ψ_target2, T, Δt;
- ipopt_options=IpoptOptions(print_level=1),
- piccolo_options=PiccoloOptions(verbose=false)
-)
-
-best_trajectory_callback, best_trajectory_list = best_rollout_fidelity_callback(prob2)
-solve!(prob2, max_iter=20, callback=best_trajectory_callback)
-````
-
-fidelity of the last iterate
-
-````@example ipopt_callbacks
-@show Losses.fidelity(prob2)
-````
-
-fidelity of the best iterate
-
-````@example ipopt_callbacks
-@show QuantumCollocation.fidelity(best_trajectory_list[end], prob2.system)
-````
-
----
-
-*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*
-
diff --git a/docs/src/generated/man/problem_templates.md b/docs/src/generated/man/problem_templates.md
deleted file mode 100644
index b4841c67..00000000
--- a/docs/src/generated/man/problem_templates.md
+++ /dev/null
@@ -1,24 +0,0 @@
-```@meta
-EditURL = "../../../literate/man/problem_templates.jl"
-```
-
-# Problem Templates
-
-We provide a number of problem templates for making it simple and easy to set up and solve certain types of quantum optimal control problems. These templates all construct a `QuantumControlProblem` object. The problem templates are:
-
-## Unitary Smooth Pulse Problem
-
-```@docs
-UnitarySmoothPulseProblem
-```
-
-## Unitary Minimum Time Problem
-
-```@docs
-UnitaryMinimumTimeProblem
-```
-
----
-
-*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*
-
diff --git a/docs/src/generated/quickstart.md b/docs/src/generated/quickstart.md
deleted file mode 100644
index ff865d0b..00000000
--- a/docs/src/generated/quickstart.md
+++ /dev/null
@@ -1,125 +0,0 @@
-```@meta
-EditURL = "../../literate/quickstart.jl"
-```
-
-# Quickstart Guide
-
-To set up and solve a quantum optimal control problems we provide high level problem templates to quickly get started. For unitary gate problems, where we want to realize a gate $U_{\text{goal}}$, with a system Hamiltonian of the form,
-```math
-H(t) = H_0 + \sum_i a^i(t) H_i
-```
-there is the `UnitarySmoothPulseProblem` constructor which only requires
-- the drift Hamiltonian, $H_0$
-- the drive Hamiltonians, $\qty{H_i}$
-- the target unitary, $U_{\text{goal}}$
-- the number of timesteps, $T$
-- the (initial) time step size, $\Delta t$
-
-## Basic Usage
-
-For example, to create a problem for a single qubit $X$ gate (with a bound on the drive of $|a^i| < a_{\text{bound}}$), i.e., with system hamiltonian
-```math
-H(t) = \frac{\omega}{2} \sigma_z + a^1(t) \sigma_x + a^2(t) \sigma_y
-```
-we can do the following:
-
-````@example quickstart
-using NamedTrajectories
-using QuantumCollocation
-
-# set time parameters
-T = 100
-Δt = 0.1
-
-# use the exported gate dictionary to get the gates we need
-σx = GATES[:X]
-σy = GATES[:Y]
-σz = GATES[:Z]
-
-# define drift and drive Hamiltonians
-H_drift = 0.5 * σz
-H_drives = [σx, σy]
-
-# define target unitary
-U_goal = σx
-
-# set bound on the drive
-a_bound = 1.0
-
-# build the problem
-prob = UnitarySmoothPulseProblem(
- H_drift,
- H_drives,
- U_goal,
- T,
- Δt;
- a_bound=a_bound,
-)
-
-# solve the problem
-solve!(prob; max_iter=30)
-````
-
-The above output comes from the Ipopt.jl solver. To see the final fidelity we can use the `unitary_fidelity` function exported by QuantumCollocation.jl.
-
-````@example quickstart
-println("Final fidelity: ", unitary_rollout_fidelity(prob))
-````
-
-We can also easily plot the solutions using the `plot` function exported by NamedTrajectories.jl.
-
-````@example quickstart
-plot(prob.trajectory, [:Ũ⃗, :a])
-````
-
-## Minimum Time Problems
-
-We can also easily set up and solve a minimum time problem, where we enforce a constraint on the final fidelity:
-```math
-\mathcal{F}(U_T, U_{\text{goal}}) \geq \mathcal{F}_{\text{min}}
-```
-Using the problem we just solved we can do the following:
-
-````@example quickstart
-# final fidelity constraint
-final_fidelity = 0.99
-
-# weight on the minimum time objective
-D = 10.0
-
-prob_min_time = UnitaryMinimumTimeProblem(
- prob;
- final_fidelity=final_fidelity,
- D=D
-)
-
-solve!(prob_min_time; max_iter=30)
-````
-
-We can see that the final fidelity is indeed greater than the minimum fidelity we set.
-
-````@example quickstart
-println("Final fidelity: ", unitary_rollout_fidelity(prob_min_time))
-````
-
-and that the duration of the pulse has decreased.
-
-````@example quickstart
-initial_dur = get_times(prob.trajectory)[end]
-min_time_dur = get_times(prob_min_time.trajectory)[end]
-
-println("Initial duration: ", initial_dur)
-println("Minimum duration: ", min_time_dur)
-println("Duration decrease: ", initial_dur - min_time_dur)
-````
-
-We can also plot the solutions for the minimum time problem.
-
-````@example quickstart
-plot(prob_min_time.trajectory, [:Ũ⃗, :a])
-````
-
----
-
-*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*
-
diff --git a/docs/src/index.md b/docs/src/index.md
index d34e68eb..a2ae85ba 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -1,68 +1,103 @@
-```@meta
-CurrentModule = QuantumCollocation
+```@raw html
+
+
+ Quickly set up and solve problem templates for quantum optimal control.
+
+
+
```
# QuantumCollocation.jl
-*Direct Collocation for Quantum Optimal Control* ([arXiv](https://arxiv.org/abs/2305.03261))
+**QuantumCollocation.jl** sets up and solves *quantum control problems* as nonlinear programs (NLPs). In this context, a generic quantum control problem looks like
+```math
+\begin{aligned}
+ \arg \min_{\mathbf{Z}}\quad & J(\mathbf{Z}) \\
+ \nonumber \text{s.t.}\qquad & \mathbf{f}(\mathbf{Z}) = 0 \\
+ \nonumber & \mathbf{g}(\mathbf{Z}) \le 0
+\end{aligned}
+```
+where $\mathbf{Z}$ is a trajectory containing states and controls, from [NamedTrajectories.jl](https://github.com/kestrelquantum/NamedTrajectories.jl).
-Check out our JuliaCon 2023 talk:
+### Problem Templates
-```@raw html
-
-
-
+*Problem Templates* are reusable design patterns for setting up and solving common quantum control problems.
+
+For example, a *UnitarySmoothPulseProblem* is tasked with generating a *pulse* sequence $a_{1:T-1}$ in orderd to minimize infidelity, subject to constraints from the Schroedinger equation,
+```math
+ \begin{aligned}
+ \arg \min_{\mathbf{Z}}\quad & |1 - \mathcal{F}(U_T, U_\text{goal})| \\
+ \nonumber \text{s.t.}
+ \qquad & U_{t+1} = \exp\{- i H(a_t) \Delta t_t \} U_t, \quad \forall\, t \\
+ \end{aligned}
+```
+while a *UnitaryMinimumTimeProblem* minimizes time and constrains fidelity,
+```math
+ \begin{aligned}
+ \arg \min_{\mathbf{Z}}\quad & \sum_{t=1}^T \Delta t_t \\
+ \qquad & U_{t+1} = \exp\{- i H(a_t) \Delta t_t \} U_t, \quad \forall\, t \\
+ \nonumber & \mathcal{F}(U_T, U_\text{goal}) \ge 0.9999
+ \end{aligned}
```
-Also check out [the sequel, from JuliaCon 2024](https://www.youtube.com/watch?v=v0RPD4eSzVE&t=19980s).
+In each case, the dynamics between *knot points* $(U_t, a_t)$ and $(U_{t+1}, a_{t+1})$ are enforced as constraints on the states, which are free variables in the solver; this optimization framework is called *direct collocation*. For details of our implementation please see our award-winning IEEE QCE 2023 paper, [Direct Collocation for Quantum Optimal Control](https://arxiv.org/abs/2305.03261). If you use QuantumCollocation.jl in your work, please cite :raised_hands:!
+
+Problem templates give the user the ability to add other constraints and objective functions to this problem and solve it efficiently using [Ipopt.jl](https://github.com/jump-dev/Ipopt.jl) and [MathOptInterface.jl](https://github.com/jump-dev/MathOptInterface.jl) under the hood.
+
+## Installation
+
+This package is registered! To install, enter the Julia REPL, type `]` to enter pkg mode, and then run:
+```julia
+pkg> add QuantumCollocation
+```
## Example
+### Single Qubit Hadamard Gate
```Julia
using QuantumCollocation
T = 50
Δt = 0.2
system = QuantumSystem([PAULIS[:X], PAULIS[:Y]])
+U_goal = GATES.H
# Hadamard Gate
-prob = UnitarySmoothPulseProblem(system, GATES[:H], T, Δt)
+prob = UnitarySmoothPulseProblem(system, U_goal, T, Δt)
solve!(prob, max_iter=100)
-
-plot_unitary_populations(prob)
-```
-![Single Qubit X-Gate](assets/x_gate_unitary_populations.svg)
-
-## Motivation
-
-In quantum optimal control, we are interested in finding a pulse sequence $a_{1:T-1}$ to drive a quantum system and realize a target gate $U_{\text{goal}}$. We formulate this problem as a nonlinear program (NLP) of the form
-
-```math
-\begin{aligned}
-\underset{U_{1:T}, a_{1:T-1}, \Delta t_{1:T-1}}{\text{minimize}} & \quad \ell(U_T, U_{\text{goal}})\\
-\text{ subject to } & \quad f(U_{t+1}, U_t, a_t, \Delta t_t) = 0 \\
-\end{aligned}
-```
-
-where $f$ defines the dynamics, implicitly, as constraints on the states and controls, $U_{1:T}$ and $a_{1:T-1}$, which are both free variables in the solver. This optimization framework is called *direct collocation*. For technical details of our implementation please see our paper [Direct Collocation for Quantum Optimal Control](https://arxiv.org/abs/2305.03261).
-
-The gist of the method is that the dynamics are given by the solution to the Schrodinger equation, which results in unitary evolution given by $\exp(-i \Delta t H(a_t))$, where $H(a_t)$ is the Hamiltonian of the system and $\Delta t$ is the timestep. We can approximate this evolution using Pade approximants:
-
-```math
-\begin{aligned}
-f(U_{t+1}, U_t, a_t, \Delta t_t) &= U_{t+1} - \exp(-i \Delta t_t H(a_t)) U_t \\
-&\approx U_{t+1} - B^{-1}(a_t, \Delta t_t) F(a_t, \Delta t_t) U_t \\
-&= B(a_t, \Delta t_t) U_{t+1} - F(a_t, \Delta t_t) U_t \\
-\end{aligned}
```
-
-where $B(a_t)$ and $F(a_t)$ are the *backward* and *forward* Pade operators and are just polynomials in $H(a_t)$.
-
-This implementation is possible because direct collocation allows for the dynamics to be implicit. Since numerically calculating matrix exponentials inherently requires an approximation -- the Padé approximant is commonly used -- utilizing this formulation significantly improves performance, as, at least here, no matrix inversion is required.
-
-
-## Index
-
-```@index
-```
-
diff --git a/docs/src/lib.md b/docs/src/lib.md
index b308faf5..835b8de1 100644
--- a/docs/src/lib.md
+++ b/docs/src/lib.md
@@ -5,34 +5,19 @@
Modules = [QuantumCollocation.ProblemTemplates]
```
-## Direct Sums
-```@autodocs
-Modules = [QuantumCollocation.DirectSums]
-```
-
## Options
```@autodocs
Modules = [QuantumCollocation.Options]
```
-## Plotting
-```@autodocs
-Modules = [QuantumCollocation.Plotting]
-```
-
-## Problem Solvers
-```@autodocs
-Modules = [QuantumCollocation.ProblemSolvers]
-```
-
## Rollouts
```@autodocs
Modules = [QuantumCollocation.Rollouts]
```
-## Saving and Loading
+## Direct Sums
```@autodocs
-Modules = [QuantumCollocation.SaveLoadUtils]
+Modules = [QuantumCollocation.DirectSums]
```
## Trajectory Initialization
diff --git a/docs/src/release_notes.md b/docs/src/release_notes.md
deleted file mode 100644
index 559b03a1..00000000
--- a/docs/src/release_notes.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Release Notes
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-
-## [v0.3.1] - 2024-10-17
-
-### Fixed
-
-- Fixed and added tests to [`RydbergChainSystem`](@ref)
-
-## [v0.3.0] - 2024-10-10
-
-### Added
-
-- [`PiccoloOptions`] (@ref) to handle custome problem settings.
-
-## Changed
-
-- Refactored trajectory initialization functions
-- Improved documentation
-- Typo fixes
-
-## [v0.2.0] - 2024-02-22
-
-### Added
-
-- [`EmbeddedOperator`](@ref) to handle subspace gate optimization and leakage suppression
-- Plotting methods for unitary populations
-
-### Changed
-
-- New quantum systems interface
- - Transmon system template
-- Restructured the code base for easier quantum system and problem template development
-
-### Removed
-
-- Stale examples
-
-### Fixed
-
-- Robustness improvements objective test fixes
\ No newline at end of file
diff --git a/src/callbacks.jl b/src/callbacks.jl
index 56607dd2..5e234970 100644
--- a/src/callbacks.jl
+++ b/src/callbacks.jl
@@ -5,7 +5,7 @@ export best_unitary_rollout_fidelity_callback
export trajectory_history_callback
using NamedTrajectories
-using TestItemRunner
+using TestItems
using QuantumCollocationCore
using PiccoloQuantumObjects
diff --git a/src/direct_sums.jl b/src/direct_sums.jl
index d069b8c9..a0fff177 100644
--- a/src/direct_sums.jl
+++ b/src/direct_sums.jl
@@ -6,7 +6,7 @@ export get_suffix_label
export direct_sum
using SparseArrays
-using TestItemRunner
+using TestItems
using NamedTrajectories
using QuantumCollocationCore
using PiccoloQuantumObjects
diff --git a/src/problem_templates/_problem_templates.jl b/src/problem_templates/_problem_templates.jl
index 8274cd7d..9beda1e5 100644
--- a/src/problem_templates/_problem_templates.jl
+++ b/src/problem_templates/_problem_templates.jl
@@ -14,7 +14,7 @@ using LinearAlgebra
using SparseArrays
using ExponentialAction
using JLD2
-using TestItemRunner
+using TestItems
include("unitary_smooth_pulse_problem.jl")
include("unitary_minimum_time_problem.jl")
diff --git a/src/problem_templates/quantum_state_minimum_time_problem.jl b/src/problem_templates/quantum_state_minimum_time_problem.jl
index 6b644c26..f2d3ab1a 100644
--- a/src/problem_templates/quantum_state_minimum_time_problem.jl
+++ b/src/problem_templates/quantum_state_minimum_time_problem.jl
@@ -22,7 +22,7 @@ or
- `D=1.0`: The cost weight on the time.
- `ipopt_options::IpoptOptions=IpoptOptions()`: The Ipopt options.
- `piccolo_options::PiccoloOptions=PiccoloOptions()`: The Piccolo options.
-- `kwargs...`: Additional keyword arguments, passed to [`QuantumControlProblem`](@ref).
+- `kwargs...`: Additional keyword arguments, passed to `QuantumControlProblem`.
"""
function QuantumStateMinimumTimeProblem end
diff --git a/src/quantum_system_templates/_quantum_system_templates.jl b/src/quantum_system_templates/_quantum_system_templates.jl
index 5621166a..593d9dee 100644
--- a/src/quantum_system_templates/_quantum_system_templates.jl
+++ b/src/quantum_system_templates/_quantum_system_templates.jl
@@ -2,7 +2,7 @@ module QuantumSystemTemplates
using PiccoloQuantumObjects
using LinearAlgebra
-using TestItemRunner
+using TestItems
include("transmons.jl")
include("rydberg.jl")
diff --git a/src/quantum_system_templates/rydberg.jl b/src/quantum_system_templates/rydberg.jl
index cc59e0b2..761423c7 100644
--- a/src/quantum_system_templates/rydberg.jl
+++ b/src/quantum_system_templates/rydberg.jl
@@ -10,6 +10,7 @@ function generate_pattern(N::Int, i::Int)
end
return join(qubits)
end
+
function generate_pattern_with_gap(N::Int, i::Int, gap::Int)
# Create an array filled with 'I'
qubits = fill('I', N)
@@ -29,6 +30,7 @@ function lift(x::Char,i::Int, N::Int)
qubits[i] = x
return join(qubits)
end
+
@doc raw"""
RydbergChainSystem(;
N::Int=3, # number of atoms
diff --git a/src/rollouts.jl b/src/rollouts.jl
index 96755faa..214131e4 100644
--- a/src/rollouts.jl
+++ b/src/rollouts.jl
@@ -17,7 +17,7 @@ using PiccoloQuantumObjects
using ExponentialAction
using LinearAlgebra
using ProgressMeter
-using TestItemRunner
+using TestItems
using ForwardDiff
# ----------------------------------------------------------------------------- #
diff --git a/src/trajectory_initialization.jl b/src/trajectory_initialization.jl
index fdf7227d..c8d0f57a 100644
--- a/src/trajectory_initialization.jl
+++ b/src/trajectory_initialization.jl
@@ -10,7 +10,7 @@ using NamedTrajectories
using Distributions
using ExponentialAction
using LinearAlgebra
-using TestItemRunner
+using TestItems
using ..Isomorphisms
using ..QuantumSystems
diff --git a/src/trajectory_interpolations.jl b/src/trajectory_interpolations.jl
index 1e16e21d..aa4df8b4 100644
--- a/src/trajectory_interpolations.jl
+++ b/src/trajectory_interpolations.jl
@@ -5,7 +5,7 @@ export DataInterpolation
using NamedTrajectories
using Interpolations: Extrapolation, constant_interpolation, linear_interpolation
-using TestItemRunner
+using TestItems
struct DataInterpolation