Skip to content

Commit

Permalink
Merge pull request #3 from aTrotier/opti
Browse files Browse the repository at this point in the history
Opti
  • Loading branch information
aTrotier authored Aug 24, 2023
2 parents 3c5d677 + 1e9e511 commit 8a97ada
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 38 deletions.
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ julia = "1.9"
[extras]
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"

[targets]
test = ["Test", "ForwardDiff"]
test = ["Test", "ForwardDiff","BenchmarkTools"]
21 changes: 21 additions & 0 deletions docs/lit/01-autoDiff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,27 @@ lines(TE_vec,df,axis =(;title = "dS/dT2", xlabel="TE [ms]"))
j = ForwardDiff.jacobian(x -> MESE_EPG(x,T1,TE,ETL,deltaB1),[T2])
# ## Reproducibility

# ## Derivation along multiple parameters
function MESE_EPG2(T2T1,TE,ETL,delta)
T2,T1 = T2T1
T = complex(eltype(T2))
E = EPGStates([T(0.0)],[T(0.0)],[T(1.0)])
echo_vec = Vector{Complex{eltype(T2)}}()

E = epgRotation(E,pi/2*delta, pi/2)
##loop over refocusing-pulses
for i = 1:ETL
E = epgDephasing(E,1)
E = epgRelaxation(E,TE,T1,T2)
E = epgRotation(E,pi*delta,0.0)
E = epgDephasing(E,1)
push!(echo_vec,E.Fp[1])
end

return abs.(echo_vec)
end

j2 = ForwardDiff.jacobian(x -> MESE_EPG2(x,TE,ETL,deltaB1),[T2,T1])
# This page was generated with the following version of Julia:
using InteractiveUtils
io = IOBuffer();
Expand Down
79 changes: 60 additions & 19 deletions src/RegularEPG.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export epgDephasing,epgRelaxation,epgRotation,rfRotation
export epgDephasing!,epgRelaxation!,epgRotation!, epgThreshold!
export rfRotation
export EPGStates, getStates

"""
Expand Down Expand Up @@ -50,46 +51,71 @@ function getStates(E::EPGStates)
return stack([E.Fp,E.Fn,E.Z],dims=1)
end

function Base.show(io::IO, E::EPGStates{T} ) where {T}
println(io, "EPGStates struct with fields : Fp, Fn, Z")
display(getStates(E))
end

"""
epgDephasing(E::EPGStates, n=1) where T
epgDephasing!(E::EPGStates, n=1) where T
shifts the transverse dephasing states `F` corresponding to n dephasing-cycles.
n can be any integer
"""
function epgDephasing(E::EPGStates, n::Int=1)
function epgDephasing!(E::EPGStates, n::Int=1,threshold::Real=10e-6)

if(abs(n)>1)
for i in 1:abs(n)
E = epgDephasing(E, (n > 0 ? +1 : -1))
E = epgDephasing!(E, (n > 0 ? +1 : -1))
end
elseif(n == 1 || n == -1)
push!(E.Fp,0)
push!(E.Fn,0)
push!(E.Z,0)

if n == 1
E.Fp = circshift(E.Fp,+1)# Shift positive F states right
E.Fn = circshift(E.Fn,-1) # Shift negative F* states left
E.Fp[:] = circshift(E.Fp,+1)# Shift positive F states right
E.Fn[:] = circshift(E.Fn,-1) # Shift negative F* states left

# Update extremal states: F_{+0} using F*_{-0}, F*_{-max+1}=0
E.Fp[1] = conj(E.Fn[1]);
E.Fn[end] = 0;
else #
E.Fp = circshift(E.Fp,-1)# Shift positive F states right
E.Fn = circshift(E.Fn,+1) # Shift negative F* states left
E.Fp[:] = circshift(E.Fp,-1)# Shift positive F states right
E.Fn[:] = circshift(E.Fn,+1) # Shift negative F* states left

# Update extremal states: F_{+0} using F*_{-0}, F*_{-max+1}=0
E.Fn[1] = conj(E.Fp[1]);
E.Fp[end] = 0;
end

end

E = epgThreshold!(E,threshold)
return E
end

#=
function epgDephasing(E::EPGStates, n::Int,threshold::Real)
E = epgDephasing(E, n)
E = epgThreshold(E,threshold)
end
=#
function epgThreshold!(E::EPGStates,threshold::Real)
threshold²=threshold^2
for i in length(E.Fp):-1:2
if abs.(E.Fp[i]^2 + E.Fn[i]^2 + E.Z[i]^2) < threshold²
pop!(E.Fp)
pop!(E.Fn)
pop!(E.Z)
else
return E
end
end
return E
end

"""
epgRelaxation(E::EPGStates,t,T1, T2)
epgRelaxation!(E::EPGStates,t,T1, T2)
applies relaxation matrices to a set of EPG states.
Expand All @@ -99,13 +125,11 @@ applies relaxation matrices to a set of EPG states.
* `T1::AbstractFloat` - T1
* `T2::AbstractFloat` - T2
"""
function epgRelaxation(E::EPGStates,t,T1, T2)
function epgRelaxation!(E::EPGStates,t,T1, T2)
@. E.Fp = exp(-t/T2) * E.Fp
@. E.Fn = exp(-t/T2) * E.Fn
relaxedZ = fill!(copy(E.Z), 0)
@. relaxedZ[2:end] = exp(-t/T1) * E.Z[2:end]
relaxedZ[1] = exp.(-t./T1) * (E.Z[1]-1.0) + 1.0
E.Z = relaxedZ
@. E.Z[2:end] = exp(-t/T1) * E.Z[2:end]
E.Z[1] = exp.(-t./T1) * (E.Z[1]-1.0) + 1.0
return E
end

Expand All @@ -126,7 +150,7 @@ end


"""
epgRotation(E::EPGStates, alpha::Float64, phi::Float64=0.0)
epgRotation!(E::EPGStates, alpha::Float64, phi::Float64=0.0)
applies Bloch-rotation (<=> RF pulse) to a set of EPG states.
Expand All @@ -135,14 +159,31 @@ applies Bloch-rotation (<=> RF pulse) to a set of EPG states.
* `alpha::Float64` - flip angle of the RF pulse (rad)
* `phi::Float64=0.0` - phase of the RF pulse (rad)
"""
function epgRotation(E::EPGStates, alpha::Real, phi::Real=0.0)
function epgRotation!(E::EPGStates, alpha::Real, phi::Real=0.0)
R = rfRotation(alpha, phi)

epgRotation!(E, R)

return E
end

"""
epgRotation!(E::EPGStates, R::Matrix)
applies rotation matrix from `rfRotation` function to the EPGStates
# Arguments
* `E::EPGStates``
* `R::Matrix` - rotation Matrix (rad)
"""
function epgRotation!(E::EPGStates, R::Matrix)
# apply rotation to all states per default
n = length(E.Z) # numStates

R = rfRotation(alpha, phi)
for i = 1:n
E.Fp[i],E.Fn[i],E.Z[i] = R*[E.Fp[i]; E.Fn[i]; E.Z[i]]
end

return E
end
end

67 changes: 56 additions & 11 deletions test/epg/test_regular.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
function MESE_EPG_thresh(T2,T1,TE,ETL,delta,thresh)
T = eltype(complex(T2))
E = EPGStates([T(0.0)],[T(0.0)],[T(1.0)])
echo_vec = Vector{Complex{eltype(T2)}}()

epgRotation!(E,pi/2*delta, pi/2)
# loop over refocusing-pulses
R = rfRotation(pi*delta,0.0)
for i = 1:ETL
epgDephasing!(E,1,thresh)
epgRelaxation!(E,TE,T1,T2)
epgRotation!(E,R)
epgDephasing!(E,1,thresh)
push!(echo_vec,E.Fp[1])
end

return abs.(echo_vec)
end

@testset "EPG" begin
# test empty
E=EPGStates()
Expand All @@ -10,40 +29,66 @@

# test pulse
E=EPGStates()
E = epgRotation(E,deg2rad(47),deg2rad(23))
epgRotation!(E,deg2rad(47),deg2rad(23))
@test getStates(E) [
0.2857626571584661 - im*0.6732146319308543,
0.2857626571584661 + im*0.6732146319308543,
0.6819983600624985]

#test positive gradient
E = epgDephasing(E,1)
epgDephasing!(E,1)
@test getStates(E) [[0, 0, 0.6819983600624985];;
[0.2857626571584661 - im * 0.6732146319308543, 0, 0]]

# test negative gradient
E = EPGStates()
E = epgRotation(E,deg2rad(47),deg2rad(23))
E = epgDephasing(E,-1)
epgRotation!(E,deg2rad(47),deg2rad(23))
epgDephasing!(E,-1)
@test getStates(E) [[0, 0, 0.6819983600624985];;
[0, 0.2857626571584661 + im * 0.6732146319308543, 0]]

# test multiple gradient
E = EPGStates()
E = epgRotation(E,deg2rad(47),deg2rad(23))
E = epgDephasing(E,-2)
E = epgRotation(E,deg2rad(47),deg2rad(23))
E = epgDephasing(E,1)
epgRotation!(E,deg2rad(47),deg2rad(23))
epgDephasing!(E,-2)
epgRotation!(E,deg2rad(47),deg2rad(23))
epgDephasing!(E,1)
@test getStates(E) [[0, 0, 0.4651217631279373];;
[0.19488966354917586-im*0.45913127494692113, 0.240326160353821+im*0.5661729534388877,0];;
[0, 0, -0.26743911843603135];;
[-0.045436496804645087+im*0.10704167849196657, 0, 0]]

# test relaxation
E = EPGStates()
E = epgRotation(E,deg2rad(47),deg2rad(23))
E = epgDephasing(E,1)
E = epgRelaxation(E,10,1000,100)
epgRotation!(E,deg2rad(47),deg2rad(23))
epgDephasing!(E,1)
epgRelaxation!(E,10,1000,100)
@test getStates(E) [[0, 0, 0.6851625292479138];;
[0.2585687448743616 - im*0.6091497893403431, 0, 0]]

# test threshold
E = EPGStates([0+0*im,0+0.5im,0+0.01im],[0+0*im,0+0.5im,0+0.01im],[1+0*im,0+0im,0+0.0im])
epgDephasing!(E,1,10e-2)

@test getStates(E) [[0-0.5im, 0+0.5im, 1];;
[0, 0+0.01im, 0];;
[0.5im, 0,0]]

# benchmark

b = @benchmark MESE_EPG_thresh(60.0,1000.0,7.0,50,0.9,0.0)
@info "without threshold (0.0) :\n
time = $(median(b).time/1000) us\n
memory = $(median(b).memory)\n
allocs = $(median(b).allocs)\n
gctimes = $(median(b).gctime) ns\n"

b = @benchmark MESE_EPG_thresh(60.0,1000.0,7.0,50,0.9,10e-6)
@info "With threshold 10e-6 :\n
time = $(median(b).time/1000) us\n
memory = $(median(b).memory)\n
allocs = $(median(b).allocs)\n
gctimes = $(median(b).gctime) ns\n"


end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using EPGsim
using Test
using ForwardDiff
using BenchmarkTools

@testset "EPGsim.jl" begin
include("epg/test_regular.jl")
Expand Down
14 changes: 7 additions & 7 deletions test/test_AD.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ function MESE_EPG(T2,T1,TE,ETL,delta)
E = EPGStates([T(0.0)],[T(0.0)],[T(1.0)])
echo_vec = Vector{Complex{eltype(T2)}}()

E = epgRotation(E,pi/2*delta, pi/2)
epgRotation!(E,pi/2*delta, pi/2)
# loop over refocusing-pulses
for i = 1:ETL
E = epgDephasing(E,1)
E = epgRelaxation(E,TE,T1,T2)
E = epgRotation(E,pi*delta,0.0)
E = epgDephasing(E,1)
epgDephasing!(E,1)
epgRelaxation!(E,TE,T1,T2)
epgRotation!(E,pi*delta,0.0)
epgDephasing!(E,1)
push!(echo_vec,E.Fp[1])
end

Expand All @@ -22,9 +22,9 @@ end
#amp = MESE_EPG(60.0,1000.0,7,50,1) # Not used
T2 = 60.0
T1 = 1000.0
TE = 7
TE = 7.0
ETL = 50
deltaB1 = 1
deltaB1 = 1.0

# analytic gradient
TE_vec = TE:TE:TE*50
Expand Down

0 comments on commit 8a97ada

Please sign in to comment.