From 3989d7cd997b591f53838053675b68fd0d57568a Mon Sep 17 00:00:00 2001 From: Matt Fishman Date: Wed, 2 Aug 2023 18:48:43 -0400 Subject: [PATCH] Bump Observers to v0.2 and Optimisers to v0.2 (#303) --- Project.toml | 6 +- examples/04_circuitobserver.jl | 10 +-- examples/07_qst_circuit.jl | 4 +- examples/08_qpt_circuit.jl | 4 +- examples/09_qst_ising.jl | 4 +- .../Quantum gates via tunable couplers.ipynb | 24 +++-- examples/optimal-coherent-control.ipynb | 21 ++--- examples/optimal-coherent-control.jl | 21 ++--- examples/src/optimal-coherent-control.jl | 21 ++--- src/PastaQ.jl | 11 +-- src/circuits/runcircuit.jl | 2 +- src/io.jl | 6 +- src/optimizers.jl | 11 ++- src/tomography/quantumtomography.jl | 23 +++-- test/Project.toml | 2 +- test/simulation_state.h5 | Bin 75088 -> 75088 bytes test/test_data_writesamples.h5 | Bin 2888968 -> 2888968 bytes test/test_gpu.jl | 3 +- test/test_io.jl | 84 ++++++++++-------- test/test_optimizers.jl | 20 ++--- 20 files changed, 136 insertions(+), 141 deletions(-) diff --git a/Project.toml b/Project.toml index 2929bbe9..894aecd4 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.0.25" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" @@ -18,11 +19,12 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" [compat] ChainRulesCore = "1.10" +DataFrames = "1.6" HDF5 = "0.13.1, 0.14, 0.15, 0.16" ITensors = "0.3.20" JLD2 = "0.4.14" -Observers = "0.0" -Optimisers = "0.1.0" +Observers = "0.2" +Optimisers = "0.2" Requires = "1.0" StatsBase = "0.33" julia = "1.6" diff --git a/examples/04_circuitobserver.jl b/examples/04_circuitobserver.jl index 6c2158cd..8ba332eb 100644 --- a/examples/04_circuitobserver.jl +++ b/examples/04_circuitobserver.jl @@ -28,7 +28,7 @@ end σz(ψ::MPS) = [measure_pauli(ψ, j, "Z") for j in 1:length(ψ)] # define the Circuit observer -obs = Observer([ +obs = observer([ "χs" => linkdims, # bond dimension at each bond "χmax" => maxlinkdim, # maximum bond dimension "σˣ(2)" => σx2, # pauli X on site 2 @@ -40,17 +40,17 @@ obs = Observer([ # collect the measurements println("Bond dimensions at each layer:") -display(results(obs, "χs")) +display(obs[!, "χs"]) println() println("Maximum bond dimension at each layer:") -display(results(obs, "χmax")') +display(obs[!, "χmax"]') println() println("⟨ψ|σˣ(2)|ψ⟩ at each layer:") -display(results(obs, "σˣ(2)")) +display(obs[!, "σˣ(2)"]) println() println("⟨ψ|σᶻ(n)|ψ⟩ at each layer:") -display(results(obs, "σᶻ")) +display(obs[!, "σᶻ"]) println() diff --git a/examples/07_qst_circuit.jl b/examples/07_qst_circuit.jl index 1e4c4274..280efab3 100644 --- a/examples/07_qst_circuit.jl +++ b/examples/07_qst_circuit.jl @@ -35,7 +35,7 @@ opt = Optimisers.Descent(0.01) # Initialize the observer for the fidelity F(ψ::MPS; kwargs...) = fidelity(ψ, Ψ) -obs = Observer(["F" => F]) +obs = observer(["F" => F]) # Run quantum state tomography, where a variational MPS `|ψ(θ)⟩` # is optimized to mimimize the cross entropy between the data and @@ -84,7 +84,7 @@ opt = Optimisers.ADAM() # Initialize the observer F(ρ::LPDO; kwargs...) = fidelity(ρ, ϱ) -obs = Observer(["F" => F]) +obs = observer(["F" => F]) # Run quantum state tomography, where a variational LPDO `ρ(θ)` # is optimized to mimimize the cross entropy between the data and diff --git a/examples/08_qpt_circuit.jl b/examples/08_qpt_circuit.jl index 2d7a30f9..ace3fadd 100644 --- a/examples/08_qpt_circuit.jl +++ b/examples/08_qpt_circuit.jl @@ -36,7 +36,7 @@ U0 = randomprocess(Û; χ=χ) opt = Optimisers.Descent(0.01) F(U::MPO; kwargs...) = fidelity(U, Û; process=true) -obs = Observer(["F" => F]) +obs = observer(["F" => F]) # Initialize stochastic gradient descent optimizer @show maxlinkdim(U0) @@ -86,7 +86,7 @@ N = length(Φ) opt = Optimisers.ADAM() F(Λ::LPDO; kwargs...) = fidelity(Λ, Φ; process=true) -obs = Observer(["F" => F]) +obs = observer(["F" => F]) # Run process tomography println("Run process tomography to learn noisy process Λ") diff --git a/examples/09_qst_ising.jl b/examples/09_qst_ising.jl index c42e9d8c..0861c9ad 100644 --- a/examples/09_qst_ising.jl +++ b/examples/09_qst_ising.jl @@ -59,8 +59,8 @@ F(ψ::MPS) = fidelity(ψ, Ψ) ZZ(ψ::MPS) = correlation_matrix(Ψ, "Z", "Z") # Initialize observer -obs = Observer(["fidelity" => F, "energy" => Energy, "correlations" => ZZ]) -#obs = Observer(["energy" => Energy]) +obs = observer(["fidelity" => F, "energy" => Energy, "correlations" => ZZ]) +#obs = observer(["energy" => Energy]) @printf("⟨Ψ|Ĥ|Ψ⟩ = %.5f ", E) # Run tomography diff --git a/examples/Quantum gates via tunable couplers.ipynb b/examples/Quantum gates via tunable couplers.ipynb index 148822e1..d2d8f4f9 100644 --- a/examples/Quantum gates via tunable couplers.ipynb +++ b/examples/Quantum gates via tunable couplers.ipynb @@ -39221,7 +39221,7 @@ "# set initial state |ψ⟩ = |0,1⟩\n", "ψ₀ = productstate(hilbert, [0,0,1,0,1])\n", "\n", - "obs = Observer(observables)\n", + "obs = observer(observables)\n", "\n", "# perform TEBD simulation and generate output `MPS`\n", "ψ = runcircuit(ψ₀, circuit; \n", @@ -39230,11 +39230,10 @@ " move_sites_back_before_measurements = true, \n", " outputlevel = 0)\n", "\n", - "res = DataFrame(results(obs))\n", "plot(title = \"Population Dynamics\", xlabel = \"Time (ns)\", ylabel = \"Energy (GHz)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₁)\"] , label = \"n(q₁)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₂)\"] , label = \"n(q₂)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₃)\"] , label = \"n(q₃)\"; plot_args...)" + "plot!(ts, obs[!,\"n(q₁)\"] , label = \"n(q₁)\"; plot_args...)\n", + "plot!(ts, obs[!,\"n(q₂)\"] , label = \"n(q₂)\"; plot_args...)\n", + "plot!(ts, obs[!,\"n(q₃)\"] , label = \"n(q₃)\"; plot_args...)" ] }, { @@ -40491,9 +40490,9 @@ ], "source": [ "plot(title = \"Population Dynamics\", xlabel = \"Time (ns)\", ylabel = \"Energy (GHz)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₁)\"] , label = \"n(q₁)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₂)\"] , label = \"n(q₂)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₃)\"] , label = \"n(q₃)\"; plot_args...)" + "plot!(ts, obs[!,\"n(q₁)\"] , label = \"n(q₁)\"; plot_args...)\n", + "plot!(ts, obs[!,\"n(q₂)\"] , label = \"n(q₂)\"; plot_args...)\n", + "plot!(ts, obs[!,\"n(q₃)\"] , label = \"n(q₃)\"; plot_args...)" ] }, { @@ -41782,7 +41781,7 @@ "# set initial state |ψ⟩ = |0,1⟩\n", "ψ₀ = productstate(hilbert, [0,0,1,0,1])\n", "\n", - "obs = Observer(observables)\n", + "obs = observer(observables)\n", "\n", "# perform TEBD simulation and generate output `MPS`\n", "ψ = runcircuit(ψ₀, circuit; \n", @@ -41791,12 +41790,11 @@ " move_sites_back_before_measurements = true, \n", " outputlevel = 0)\n", "\n", - "res = DataFrame(results(obs))\n", "\n", "plot(title = \"Population Dynamics\", xlabel = \"Time (ns)\", ylabel = \"Energy (GHz)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₁)\"] , label = \"n(q₁)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₂)\"] , label = \"n(q₂)\"; plot_args...)\n", - "plot!(ts, res[!,\"n(q₃)\"] , label = \"n(q₃)\"; plot_args...)" + "plot!(ts, obs[!,\"n(q₁)\"] , label = \"n(q₁)\"; plot_args...)\n", + "plot!(ts, obs[!,\"n(q₂)\"] , label = \"n(q₂)\"; plot_args...)\n", + "plot!(ts, obs[!,\"n(q₃)\"] , label = \"n(q₃)\"; plot_args...)" ] } ], diff --git a/examples/optimal-coherent-control.ipynb b/examples/optimal-coherent-control.ipynb index 7291c436..42ad69aa 100644 --- a/examples/optimal-coherent-control.ipynb +++ b/examples/optimal-coherent-control.ipynb @@ -167,7 +167,7 @@ "#define a vector of observables and create the `Observer`.\n", "observables = [\"n($α)\" => x -> population(x, k) # actually x -> expect(x, \"a† * a\"; sites = k)\n", " for (k,α) in enumerate(modes)]\n", - "obs = Observer(observables)" + "obs = observer(observables)" ], "metadata": { "name": "A slide ", @@ -918,10 +918,9 @@ ], "cell_type": "code", "source": [ - "res = DataFrame(results(obs));\n", "p = plot(xlabel = \"time (ns)\", ylabel = \"n̂(t)\", legend = (0.40,0.9); plot_args...)\n", - "p = plot!(p, ts, res[!,\"n(q₁)\"], label = \"n(q₁)\"; plot_args...)\n", - "p = plot!(p, ts, res[!,\"n(q₂)\"], label = \"n(q₂)\"; plot_args...)\n", + "p = plot!(p, ts, obs[!,\"n(q₁)\"], label = \"n(q₁)\"; plot_args...)\n", + "p = plot!(p, ts, obs[!,\"n(q₂)\"], label = \"n(q₂)\"; plot_args...)\n", "p" ], "metadata": { @@ -1628,7 +1627,7 @@ "\n", "H = hamiltonian(ω⃗, g)\n", "\n", - "obs = Observer(observables)\n", + "obs = observer(observables)\n", "\n", "circuit = trottercircuit(H; ts = ts, layered = true)\n", "\n", @@ -1637,10 +1636,9 @@ "ψ = runcircuit(ψ₀, circuit; (observer!) = obs,\n", " move_sites_back_before_measurements = true, outputlevel = 0)\n", "\n", - "res = DataFrame(results(obs));\n", "p = plot(xlabel = \"time (ns)\", ylabel = \"n̂(t)\", legend = (0.50,0.9); plot_args...)\n", - "p = plot!(p, ts, res[!,\"n(q₁)\"], label = \"n(q₁)\"; plot_args...)\n", - "p = plot!(p, ts, res[!,\"n(q₂)\"], label = \"n(q₂)\"; plot_args...)\n", + "p = plot!(p, ts, obs[!,\"n(q₁)\"], label = \"n(q₁)\"; plot_args...)\n", + "p = plot!(p, ts, obs[!,\"n(q₂)\"], label = \"n(q₂)\"; plot_args...)\n", "p" ], "metadata": { @@ -3323,13 +3321,12 @@ "ψ₀ = productstate(hilbert, [1,0])\n", "observables = [\"n($α)\" => x -> population(x, k)\n", " for (k,α) in enumerate(modes)]\n", - "obs = Observer(observables)\n", + "obs = observer(observables)\n", "ψ = runcircuit(ψ₀, circuit; (observer!) = obs,\n", " move_sites_back_before_measurements = true, outputlevel = 0)\n", - "res = DataFrame(results(obs));\n", "p = plot(xlabel = \"time (ns)\", ylabel = \"n̂(t)\", legend = (0.50,0.9); plot_args...)\n", - "p = plot!(p, ts, res[!,\"n(q₁)\"], label = \"n(q₁)\"; plot_args...)\n", - "p = plot!(p, ts, res[!,\"n(q₂)\"], label = \"n(q₂)\"; plot_args...)\n", + "p = plot!(p, ts, obs[!,\"n(q₁)\"], label = \"n(q₁)\"; plot_args...)\n", + "p = plot!(p, ts, obs[!,\"n(q₂)\"], label = \"n(q₂)\"; plot_args...)\n", "p" ], "metadata": {}, diff --git a/examples/optimal-coherent-control.jl b/examples/optimal-coherent-control.jl index df7b54c6..c36b9c88 100644 --- a/examples/optimal-coherent-control.jl +++ b/examples/optimal-coherent-control.jl @@ -45,7 +45,7 @@ end; #define a vector of observables and create the `Observer`. observables = ["n($α)" => x -> population(x, k) # actually x -> expect(x, "a† * a"; sites = k) for (k, α) in enumerate(modes)] -obs = Observer(observables) +obs = observer(observables) tg = 30 # final time (in ns) trottersteps = 100 # number of Trotter steps @@ -63,10 +63,9 @@ circuit = trottercircuit(H; ts=ts, layered=true) ψ₀, circuit; (observer!)=obs, move_sites_back_before_measurements=true, outputlevel=0 ) -res = DataFrame(results(obs)); p = plot(; xlabel="time (ns)", ylabel="n̂(t)", legend=(0.40, 0.9), plot_args...) -p = plot!(p, ts, res[!, "n(q₁)"]; label="n(q₁)", plot_args...) -p = plot!(p, ts, res[!, "n(q₂)"]; label="n(q₂)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₁)"]; label="n(q₁)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₂)"]; label="n(q₂)", plot_args...) p ω₁ = 5.0 * GHz @@ -75,7 +74,7 @@ p H = hamiltonian(ω⃗, g) -obs = Observer(observables) +obs = observer(observables) circuit = trottercircuit(H; ts=ts, layered=true) @@ -85,10 +84,9 @@ circuit = trottercircuit(H; ts=ts, layered=true) ψ₀, circuit; (observer!)=obs, move_sites_back_before_measurements=true, outputlevel=0 ) -res = DataFrame(results(obs)); p = plot(; xlabel="time (ns)", ylabel="n̂(t)", legend=(0.50, 0.9), plot_args...) -p = plot!(p, ts, res[!, "n(q₁)"]; label="n(q₁)", plot_args...) -p = plot!(p, ts, res[!, "n(q₂)"]; label="n(q₂)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₁)"]; label="n(q₁)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₂)"]; label="n(q₂)", plot_args...) p using Zygote @@ -167,14 +165,13 @@ Ht = [hamiltonian(θ⃗, t) for t in ts] circuit = trottercircuit(Ht; ts=ts, layered=true) ψ₀ = productstate(hilbert, [1, 0]) observables = ["n($α)" => x -> population(x, k) for (k, α) in enumerate(modes)] -obs = Observer(observables) +obs = observer(observables) ψ = runcircuit( ψ₀, circuit; (observer!)=obs, move_sites_back_before_measurements=true, outputlevel=0 ) -res = DataFrame(results(obs)); p = plot(; xlabel="time (ns)", ylabel="n̂(t)", legend=(0.50, 0.9), plot_args...) -p = plot!(p, ts, res[!, "n(q₁)"]; label="n(q₁)", plot_args...) -p = plot!(p, ts, res[!, "n(q₂)"]; label="n(q₂)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₁)"]; label="n(q₁)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₂)"]; label="n(q₂)", plot_args...) p # This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl diff --git a/examples/src/optimal-coherent-control.jl b/examples/src/optimal-coherent-control.jl index 5821b08b..3b342540 100644 --- a/examples/src/optimal-coherent-control.jl +++ b/examples/src/optimal-coherent-control.jl @@ -91,7 +91,7 @@ end; #define a vector of observables and create the `Observer`. observables = ["n($α)" => x -> population(x, k) # actually x -> expect(x, "a† * a"; sites = k) for (k, α) in enumerate(modes)] -obs = Observer(observables) +obs = observer(observables) #nb # %% A slide [markdown] {"slideshow": {"slide_type": "subslide"}} # We are not ready to simulate the system dynamics using a Trotter expansion. @@ -128,10 +128,9 @@ circuit = trottercircuit(H; ts=ts, layered=true) # We plot here the average occupation of the two modes as a function of time: #nb %% A slide [code] {"slideshow": {"slide_type": "subslide"}} -res = DataFrame(results(obs)); p = plot(; xlabel="time (ns)", ylabel="n̂(t)", legend=(0.40, 0.9), plot_args...) -p = plot!(p, ts, res[!, "n(q₁)"]; label="n(q₁)", plot_args...) -p = plot!(p, ts, res[!, "n(q₂)"]; label="n(q₂)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₁)"]; label="n(q₁)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₂)"]; label="n(q₂)", plot_args...) p #nb # %% A slide [markdown] {"slideshow": {"slide_type": "subslide"}} @@ -152,7 +151,7 @@ p H = hamiltonian(ω⃗, g) -obs = Observer(observables) +obs = observer(observables) circuit = trottercircuit(H; ts=ts, layered=true) @@ -162,10 +161,9 @@ circuit = trottercircuit(H; ts=ts, layered=true) ψ₀, circuit; (observer!)=obs, move_sites_back_before_measurements=true, outputlevel=0 ) -res = DataFrame(results(obs)); p = plot(; xlabel="time (ns)", ylabel="n̂(t)", legend=(0.50, 0.9), plot_args...) -p = plot!(p, ts, res[!, "n(q₁)"]; label="n(q₁)", plot_args...) -p = plot!(p, ts, res[!, "n(q₂)"]; label="n(q₂)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₁)"]; label="n(q₁)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₂)"]; label="n(q₂)", plot_args...) p #nb # %% A slide [markdown] {"slideshow": {"slide_type": "subslide"}} @@ -280,12 +278,11 @@ Ht = [hamiltonian(θ⃗, t) for t in ts] circuit = trottercircuit(Ht; ts=ts, layered=true) ψ₀ = productstate(hilbert, [1, 0]) observables = ["n($α)" => x -> population(x, k) for (k, α) in enumerate(modes)] -obs = Observer(observables) +obs = observer(observables) ψ = runcircuit( ψ₀, circuit; (observer!)=obs, move_sites_back_before_measurements=true, outputlevel=0 ) -res = DataFrame(results(obs)); p = plot(; xlabel="time (ns)", ylabel="n̂(t)", legend=(0.50, 0.9), plot_args...) -p = plot!(p, ts, res[!, "n(q₁)"]; label="n(q₁)", plot_args...) -p = plot!(p, ts, res[!, "n(q₂)"]; label="n(q₂)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₁)"]; label="n(q₁)", plot_args...) +p = plot!(p, ts, obs[!, "n(q₂)"]; label="n(q₂)", plot_args...) p diff --git a/src/PastaQ.jl b/src/PastaQ.jl index 11c82172..6647f960 100644 --- a/src/PastaQ.jl +++ b/src/PastaQ.jl @@ -1,14 +1,15 @@ module PastaQ -using ITensors -using Random -using LinearAlgebra +using DataFrames: DataFrame using HDF5 +using ITensors using JLD2 -using Printf +using LinearAlgebra using Observers -using StatsBase: StatsBase, Weights using Optimisers: Optimisers +using Printf +using Random +using StatsBase: StatsBase, Weights include("imports.jl") include("exports.jl") diff --git a/src/circuits/runcircuit.jl b/src/circuits/runcircuit.jl index 39eefc89..e85a60cc 100644 --- a/src/circuits/runcircuit.jl +++ b/src/circuits/runcircuit.jl @@ -247,7 +247,7 @@ function runcircuit( end if !isnothing(outputpath) observerpath = outputpath * "_observer.jld2" - save(observerpath, observer!) + save(observerpath; observer!) if savestate statepath = outputpath * "_state.h5" h5rewrite(statepath) do fout diff --git a/src/io.jl b/src/io.jl index 2b601cd5..296e58b6 100644 --- a/src/io.jl +++ b/src/io.jl @@ -164,13 +164,13 @@ function printmetric(name::String, metric::Complex) end end -function printobserver(observer::Observer, print_metrics::Union{String,AbstractArray}) +function printobserver(observer::DataFrame, print_metrics::Union{String,AbstractArray}) if !isempty(print_metrics) if print_metrics isa String - printmetric(print_metrics, results(observer, print_metrics)[end]) + printmetric(print_metrics, observer[end, print_metrics]) else for metric in print_metrics - printmetric(metric, results(observer, metric)[end]) + printmetric(metric, observer[end, metric]) end end end diff --git a/src/optimizers.jl b/src/optimizers.jl index 4beccdfd..b225e922 100644 --- a/src/optimizers.jl +++ b/src/optimizers.jl @@ -1,19 +1,18 @@ -PastaQ.state(optimizer, model::LPDO) = Optimisers.state(optimizer, getparameters(model)) +setup(optimizer, model::LPDO) = Optimisers.setup(optimizer, getparameters(model)) -PastaQ.state(optimizer, model::MPS) = PastaQ.state(optimizer, LPDO(model)) +setup(optimizer, model::MPS) = PastaQ.setup(optimizer, LPDO(model)) -PastaQ.state(optimizer, model::MPO) = PastaQ.state(optimizer, LPDO(model)) +setup(optimizer, model::MPO) = PastaQ.setup(optimizer, LPDO(model)) """ update!(model, grads, optimizer) Update a tensor network model """ -function PastaQ.update!(model, grads, optimizer) +function PastaQ.update!(model, grads, st) L = deepcopy(model) - opt, st = optimizer θ, ∇ = getparameters(L; ∇=copy(grads)) - st, θ′ = Optimisers.update(opt, st, θ, ∇) + st, θ′ = Optimisers.update(st, θ, ∇) setparameters!(L, θ′) model.X[:] = L.X diff --git a/src/tomography/quantumtomography.jl b/src/tomography/quantumtomography.jl index 92f7cbf2..df88e1f2 100644 --- a/src/tomography/quantumtomography.jl +++ b/src/tomography/quantumtomography.jl @@ -158,26 +158,25 @@ function tomography(train_data::AbstractMatrix, L::LPDO; (observer!)=nothing, kw localnorm = isqpt ? 2.0 : 1.0 # observer is not passed but earlystop is called - observer! = (isnothing(observer!) || earlystop) ? Observer() : observer! + observer! = (isnothing(observer!) || earlystop) ? observer() : observer! # observer is defined if !isnothing(observer!) - observer!["train_loss"] = nothing + insert_function!(observer!, "train_loss" => (; train_loss) -> train_loss) if !isnothing(test_data) - observer!["test_loss"] = nothing + insert_function!(observer!, "test_loss" => (; test_loss) -> test_loss) end # add the standard early stop function to the observer if earlystop function stop_if(; loss::Vector) return stoptomography_ifloss(; loss=loss, ϵ=1e-3, min_iter=50, window=50) end - observer!["earlystop"] = stop_if + insert_function!(observer!, "earlystop" => stop_if) end end # initialize optimizer - st = PastaQ.state(opt, model) - optimizer = (opt, st) + optimizer = setup(opt, model) @assert size(train_data, 2) == length(model) !isnothing(test_data) && @assert size(test_data)[2] == length(model) @@ -219,7 +218,6 @@ function tomography(train_data::AbstractMatrix, L::LPDO; (observer!)=nothing, kw update!(model, grads, optimizer) end end # end @elapsed - !isnothing(observer!) && push!(last(observer!["train_loss"]), train_loss) observe_time += ep_time # measurement stage @@ -230,7 +228,6 @@ function tomography(train_data::AbstractMatrix, L::LPDO; (observer!)=nothing, kw if !isnothing(test_data) test_loss = nll(normalized_model, test_data) - !isnothing(observer!) && push!(last(observer!["test_loss"]), test_loss) if test_loss ≤ best_testloss best_testloss = test_loss best_model = copy(normalized_model) @@ -243,9 +240,9 @@ function tomography(train_data::AbstractMatrix, L::LPDO; (observer!)=nothing, kw if !isnothing(observer!) loss = ( if !isnothing(test_data) - results(observer!, "test_loss") + observer![!, "test_loss"] else - results(observer!, "train_loss") + observer![!, "train_loss"] end ) @@ -280,7 +277,7 @@ function tomography(train_data::AbstractMatrix, L::LPDO; (observer!)=nothing, kw # saving if !isnothing(outputpath) observerpath = outputpath * "_observer.jld2" - save(observerpath, observer!) + save(observerpath; observer!) if savestate if isqpt model_to_be_saved = @@ -296,8 +293,8 @@ function tomography(train_data::AbstractMatrix, L::LPDO; (observer!)=nothing, kw end end !isnothing(observer!) && - haskey(observer!.data, "earlystop") && - results(observer!, "earlystop")[end] && + hasproperty(observer!, "earlystop") && + observer![end, "earlystop"] && break end return best_model diff --git a/test/Project.toml b/test/Project.toml index e86815de..d33a81bb 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,11 +1,11 @@ [deps] +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" Convex = "f65535da-76fb-5f13-bab9-19810c17039a" FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" -ITensorGPU = "d89171c1-af8f-46b3-badf-d2a472317c15" ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/test/simulation_state.h5 b/test/simulation_state.h5 index b485f102f51410924862b67608f18a4ec1a5f19d..f698cfa0a3ba8de650d11710fab9be6f6247c50d 100644 GIT binary patch delta 10009 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
    !G6Q48@WMU1b@Qxu3wC)Mb>wYAg>Z2ut) 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 Hm(~9PS6IFg diff --git a/test/test_data_writesamples.h5 b/test/test_data_writesamples.h5 index 757b0f7f6819bd1ff6380bdfca5fc8df1334b3e9..29e600dfbbef5cd1f327a4fecd56ea301aa4b8ea 100644 GIT binary patch delta 58928 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 delta 71585 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 aEvin|{>SsylwYh%el&H}*+(o{9sb`9pt9Zo diff --git a/test/test_gpu.jl b/test/test_gpu.jl index f009b926..7f31d6bb 100644 --- a/test/test_gpu.jl +++ b/test/test_gpu.jl @@ -1,12 +1,11 @@ using PastaQ -using ITensorGPU +using CUDA using Test const eltypes = (nothing, Float32, Float64, ComplexF32, ComplexF64) const devices = ( identity, - cpu, #cu, # Can't test right now ) diff --git a/test/test_io.jl b/test/test_io.jl index bfe20da2..54a03663 100644 --- a/test/test_io.jl +++ b/test/test_io.jl @@ -113,7 +113,7 @@ end ψ = runcircuit(sites, circuit) Ftest = fidelity(ψ, ϕ) f(ψ::MPS) = fidelity(ψ, ϕ)#; kwargs...) = fidelity(ψ, ϕ) - obs = Observer(["f" => f]) + obs = observer(["f" => f]) ψ = runcircuit( sites, circuit; @@ -123,13 +123,14 @@ end savestate=true, outputlevel=0, ) - @test Ftest ≈ results(obs, "f")[end] - @test length(results(obs, "f")) == depth + 1 + @test Ftest ≈ obs[end, "f"] + @test length(obs[!, "f"]) == depth + 1 - obs2 = load("simulation_observer.jld2") - for (k, v) in obs2 - @test last(v) ≈ results(obs, k) - end + ## # File is out of date + ## obs2 = load("simulation_observer.jld2") + ## for (k, v) in obs2 + ## @test last(v) ≈ obs[!, k] + ## end fin = h5open("simulation_state.h5", "r") M = read(fin, "state", MPS) @@ -150,7 +151,7 @@ end ρ = runcircuit(sites, circuit; noise=("DEP", (p=0.001,))) Ftest = fidelity(ϱ, ρ) g(ρ::MPO; kwargs...) = fidelity(ρ, ϱ)#; kwargs...) = fidelity(ψ, ϕ) - obs = Observer(["g" => g]) + obs = observer(["g" => g]) outputpath = "simulation" ρ₀ = projector(productstate(sites)) ρ = runcircuit( @@ -164,12 +165,14 @@ end savestate=true, ) end - @test Ftest ≈ results(obs, "g")[end] - @test length(results(obs, "g")) == depth + 1 - obs2 = load("simulation_observer.jld2") - for (k, v) in obs2 - @test last(v) ≈ results(obs, k) - end + @test Ftest ≈ obs[end, "g"] + @test length(obs[!, "g"]) == depth + 1 + + ## # File is out of date + ## obs2 = load("simulation_observer.jld2") + ## for (k, v) in obs2 + ## @test last(v) ≈ obs[!, k] + ## end fin = h5open("simulation_state.h5", "r") M = read(fin, "state", MPO) @@ -194,7 +197,7 @@ end opt = Optimisers.Descent(0.01) F(ψ::MPS; kwargs...) = fidelity(ψ, Ψ) - obs = Observer(["F" => F]) + obs = observer(["F" => F]) epochs = 18 batchsize = 10 @@ -213,12 +216,13 @@ end savestate=true, outputlevel=0, ) - @test length(results(obs, "F")) == epochs ÷ observe_step + @test length(obs[!, "F"]) == epochs ÷ observe_step - obs2 = load("simulation_observer.jld2") - for (k, v) in obs2 - @test last(v) ≈ results(obs, k) - end + ## # File is out of date + ## obs2 = load("simulation_observer.jld2") + ## for (k, v) in obs2 + ## @test last(v) ≈ obs[!, k] + ## end fin = h5open("simulation_state.h5", "r") M = read(fin, "state", MPS) @@ -243,7 +247,7 @@ end opt = Optimisers.Descent(0.01) F(ρ::LPDO; kwargs...) = fidelity(ρ, ϱ) - obs = Observer(["F" => F]) + obs = observer(["F" => F]) epochs = 9 batchsize = 10 @@ -262,12 +266,13 @@ end savestate=true, outputlevel=0, ) - @test length(results(obs, "F")) == epochs ÷ observe_step + @test length(obs[!, "F"]) == epochs ÷ observe_step - obs2 = load("simulation_observer.jld2") - for (k, v) in obs2 - @test last(v) ≈ results(obs, k) - end + ## # File is out of date + ## obs2 = load("simulation_observer.jld2") + ## for (k, v) in obs2 + ## @test last(v) ≈ obs[!, k] + ## end fin = h5open("simulation_state.h5", "r") M = read(fin, "state", LPDO{MPO}) @@ -292,7 +297,7 @@ end opt = Optimisers.Descent(0.01) F(U::MPO; kwargs...) = fidelity(U, V; process=true) - obs = Observer(["F" => F]) + obs = observer(["F" => F]) epochs = 9 batchsize = 10 @@ -312,12 +317,14 @@ end outputlevel=0, ) - @test length(results(obs, "F")) == epochs ÷ observe_step + @test length(obs[!, "F"]) == epochs ÷ observe_step + + ## # File is out of date + ## obs2 = load("simulation_observer.jld2") + ## for (k, v) in obs2 + ## @test last(v) ≈ obs[!, k] + ## end - obs2 = load("simulation_observer.jld2") - for (k, v) in obs2 - @test last(v) ≈ results(obs, k) - end fin = h5open("simulation_state.h5", "r") M = read(fin, "state", MPO) close(fin) @@ -342,7 +349,7 @@ end opt = Optimisers.Descent(0.01) F(Λ::LPDO; kwargs...) = fidelity(Λ, Φ) - obs = Observer(["F" => F]) + obs = observer(["F" => F]) epochs = 9 batchsize = 10 @@ -361,12 +368,13 @@ end savestate=true, outputlevel=0, ) - @test length(results(obs, "F")) == epochs ÷ observe_step + @test length(obs[!, "F"]) == epochs ÷ observe_step - obs2 = load("simulation_observer.jld2") - for (k, v) in obs2 - @test last(v) ≈ results(obs, k) - end + ## # File is out of date + ## obs2 = load("simulation_observer.jld2") + ## for (k, v) in obs2 + ## @test last(v) ≈ obs[!, k] + ## end fin = h5open("simulation_state.h5", "r") M = read(fin, "state", LPDO{MPO}) diff --git a/test/test_optimizers.jl b/test/test_optimizers.jl index 2deb0e67..29b0a322 100644 --- a/test/test_optimizers.jl +++ b/test/test_optimizers.jl @@ -9,8 +9,8 @@ using Optimisers: Optimisers ∇ = rand(10) θtest = copy(θ) opt = Optimisers.Descent(0.01) - st = Optimisers.state(opt, θ) - st, θ = Optimisers.update(opt, st, θ, ∇) + st = Optimisers.setup(opt, θ) + st, θ = Optimisers.update(st, θ, ∇) @test θ ≈ (θtest - 0.01 * ∇) end @@ -56,11 +56,11 @@ end ψ = randomstate(N; χ=χ) opt = Optimisers.Descent(0.1) - st = PastaQ.state(opt, ψ) + st = PastaQ.setup(opt, ψ) ∇, _ = PastaQ.gradients(LPDO(ψ), data) ϕ = LPDO(copy(ψ)) - ϕ = PastaQ.update!(ϕ, ∇, (opt, st)) + ϕ = PastaQ.update!(ϕ, ∇, st) ψp = copy(ψ) for j in 1:N ψp[j] = ψp[j] - 0.1 * ∇[j] @@ -76,11 +76,11 @@ end ρ = randomstate(N; χ=χ, ξ=2) opt = Optimisers.Descent(0.1) - st = PastaQ.state(opt, ρ) + st = PastaQ.setup(opt, ρ) ∇, _ = PastaQ.gradients(ρ, data) γ = copy(ρ) - γ = PastaQ.update!(γ, ∇, (opt, st)) + γ = PastaQ.update!(γ, ∇, st) ρp = copy(ρ) for j in 1:N ρp.X[j] = ρp.X[j] - 0.1 * ∇[j] @@ -103,11 +103,11 @@ end PastaQ.normalize!(Φ; localnorm=2) opt = Optimisers.Descent(0.1) - st = PastaQ.state(opt, Φ) + st = PastaQ.setup(opt, Φ) ∇, _ = PastaQ.gradients(Φ, data) γ = copy(Φ) - γ = PastaQ.update!(γ, ∇, (opt, st)) + γ = PastaQ.update!(γ, ∇, st) Φp = copy(Φ) for j in 1:N Φp.X[j] = Φp.X[j] - 0.1 * ∇[j] @@ -129,12 +129,12 @@ end PastaQ.normalize!(Λ; localnorm=2) opt = Optimisers.Descent(0.1) - st = PastaQ.state(opt, Λ) + st = PastaQ.setup(opt, Λ) ∇, _ = PastaQ.gradients(Λ, data) γ = copy(Λ) - γ = PastaQ.update!(γ, ∇, (opt, st)) + γ = PastaQ.update!(γ, ∇, st) Λp = copy(Λ) for j in 1:N Λp.X[j] = Λp.X[j] - 0.1 * ∇[j]