How to analyze Julia memory allocation and code coverage results - memory
I am writing a package for Bayesian inference using Gibbs sampling. Since these methods are generally computationally expensive, I am very concerned about the performance of my code. In fact, speed was the reason I moved from Python to Julia.
After implementing Dirichlet Process Model I analyzed the code using Coverage.jl and the --track-allocation=user commandline option.
Here is the coverage results
- #=
- DPM
-
- Dirichlet Process Mixture Models
-
- 25/08/2015
- Adham Beyki, odinay#gmail.com
-
- =#
-
- type DPM{T}
- bayesian_component::T
- K::Int64
- aa::Float64
- a1::Float64
- a2::Float64
- K_hist::Vector{Int64}
- K_zz_dict::Dict{Int64, Vector{Int64}}
-
- DPM{T}(c::T, K::Int64, aa::Float64, a1::Float64, a2::Float64) = new(c, K, aa, a1, a2,
- Int64[], (Int64 => Vector{Int64})[])
- end
1 DPM{T}(c::T, K::Int64, aa::Real, a1::Real, a2::Real) = DPM{typeof(c)}(c, K, convert(Float64, aa),
- convert(Float64, a1), convert(Float64, a2))
-
- function Base.show(io::IO, dpm::DPM)
- println(io, "Dirichlet Mixture Model with $(dpm.K) $(typeof(dpm.bayesian_component)) components")
- end
-
- function initialize_gibbs_sampler!(dpm::DPM, zz::Vector{Int64})
- # populates the cluster labels randomly
1 zz[:] = rand(1:dpm.K, length(zz))
- end
-
- function DPM_sample_hyperparam(aa::Float64, a1::Float64, a2::Float64, K::Int64, NN::Int64, iters::Int64)
-
- # resampling concentration parameter based on Escobar and West 1995
352 for n = 1:iters
3504 eta = rand(Distributions.Beta(aa+1, NN))
3504 rr = (a1+K-1) / (NN*(a2-log(NN)))
3504 pi_eta = rr / (1+rr)
-
3504 if rand() < pi_eta
0 aa = rand(Distributions.Gamma(a1+K, 1/(a2-log(eta))))
- else
3504 aa = rand(Distributions.Gamma(a1+K-1, 1/(a2-log(eta))))
- end
- end
352 aa
- end
-
- function DPM_sample_pp{T1, T2}(
- bayesian_components::Vector{T1},
- xx::T2,
- nn::Vector{Float64},
- pp::Vector{Float64},
- aa::Float64)
-
1760000 K = length(nn)
1760000 #inbounds for kk = 1:K
11384379 pp[kk] = log(nn[kk]) + logpredictive(bayesian_components[kk], xx)
- end
1760000 pp[K+1] = log(aa) + logpredictive(bayesian_components[K+1], xx)
1760000 normalize_pp!(pp, K+1)
1760000 return sample(pp[1:K+1])
- end
-
-
- function collapsed_gibbs_sampler!{T1, T2}(
- dpm::DPM{T1},
- xx::Vector{T2},
- zz::Vector{Int64},
- n_burnins::Int64, n_lags::Int64, n_samples::Int64, n_internals::Int64; max_clusters::Int64=100)
-
-
2 NN = length(xx) # number of data points
2 nn = zeros(Float64, dpm.K) # count array
2 n_iterations = n_burnins + (n_samples)*(n_lags+1)
2 bayesian_components = [deepcopy(dpm.bayesian_component) for k = 1:dpm.K+1]
2 dpm.K_hist = zeros(Int64, n_iterations)
2 pp = zeros(Float64, max_clusters)
-
2 tic()
2 for ii = 1:NN
10000 kk = zz[ii]
10000 additem(bayesian_components[kk], xx[ii])
10000 nn[kk] += 1
- end
2 dpm.K_hist[1] = dpm.K
2 elapsed_time = toq()
-
2 for iteration = 1:n_iterations
-
352 println("iteration: $iteration, KK: $(dpm.K), KK mode: $(indmax(hist(dpm.K_hist,
- 0.5:maximum(dpm.K_hist)+0.5)[2])), elapsed time: $elapsed_time")
-
352 tic()
352 #inbounds for ii = 1:NN
1760000 kk = zz[ii]
1760000 nn[kk] -= 1
1760000 delitem(bayesian_components[kk], xx[ii])
-
- # remove the cluster if empty
1760000 if nn[kk] == 0
166 println("\tcomponent $kk has become inactive")
166 splice!(nn, kk)
166 splice!(bayesian_components, kk)
166 dpm.K -= 1
-
- # shifting the labels one cluster back
830166 idx = find(x -> x>kk, zz)
166 zz[idx] -= 1
- end
-
1760000 kk = DPM_sample_pp(bayesian_components, xx[ii], nn, pp, dpm.aa)
-
1760000 if kk == dpm.K+1
171 println("\tcomponent $kk activated.")
171 push!(bayesian_components, deepcopy(dpm.bayesian_component))
171 push!(nn, 0)
171 dpm.K += 1
- end
-
1760000 zz[ii] = kk
1760000 nn[kk] += 1
1760000 additem(bayesian_components[kk], xx[ii])
- end
-
352 dpm.aa = DPM_sample_hyperparam(dpm.aa, dpm.a1, dpm.a2, dpm.K, NN, n_internals)
352 dpm.K_hist[iteration] = dpm.K
352 dpm.K_zz_dict[dpm.K] = deepcopy(zz)
352 elapsed_time = toq()
- end
- end
-
- function truncated_gibbs_sampler{T1, T2}(dpm::DPM{T1}, xx::Vector{T2}, zz::Vector{Int64},
- n_burnins::Int64, n_lags::Int64, n_samples::Int64, n_internals::Int64, K_truncation::Int64)
-
- NN = length(xx) # number of data points
- nn = zeros(Int64, K_truncation) # count array
- bayesian_components = [deepcopy(dpm.bayesian_component) for k = 1:K_truncation]
- n_iterations = n_burnins + (n_samples)*(n_lags+1)
- dpm.K_hist = zeros(Int64, n_iterations)
- states = (ASCIIString => Int64)[]
- n_states = 0
-
- tic()
- for ii = 1:NN
- kk = zz[ii]
- additem(bayesian_components[kk], xx[ii])
- nn[kk] += 1
- end
- dpm.K_hist[1] = dpm.K
-
- # constructing the sticks
- beta_VV = rand(Distributions.Beta(1.0, dpm.aa), K_truncation)
- beta_VV[end] = 1.0
- π = ones(Float64, K_truncation)
- π[2:end] = 1 - beta_VV[1:K_truncation-1]
- π = log(beta_VV) + log(cumprod(π))
-
- elapsed_time = toq()
-
- for iteration = 1:n_iterations
-
- println("iteration: $iteration, # active components: $(length(findn(nn)[1])), mode: $(indmax(hist(dpm.K_hist,
- 0.5:maximum(dpm.K_hist)+0.5)[2])), elapsed time: $elapsed_time \n", nn)
-
- tic()
- for ii = 1:NN
- kk = zz[ii]
- nn[kk] -= 1
- delitem(bayesian_components[kk], xx[ii])
-
- # resampling label
- pp = zeros(Float64, K_truncation)
- for kk = 1:K_truncation
- pp[kk] = π[kk] + logpredictive(bayesian_components[kk], xx[ii])
- end
- pp = exp(pp - maximum(pp))
- pp /= sum(pp)
-
- # sample from pp
- kk = sampleindex(pp)
- zz[ii] = kk
- nn[kk] += 1
- additem(bayesian_components[kk], xx[ii])
-
- for kk = 1:K_truncation-1
- gamma1 = 1 + nn[kk]
- gamma2 = dpm.aa + sum(nn[kk+1:end])
- beta_VV[kk] = rand(Distributions.Beta(gamma1, gamma2))
- end
- beta_VV[end] = 1.0
- π = ones(Float64, K_truncation)
- π[2:end] = 1 - beta_VV[1:K_truncation-1]
- π = log(beta_VV) + log(cumprod(π))
-
- # resampling concentration parameter based on Escobar and West 1995
- for internal_iters = 1:n_internals
- eta = rand(Distributions.Beta(dpm.aa+1, NN))
- rr = (dpm.a1+dpm.K-1) / (NN*(dpm.a2-log(NN)))
- pi_eta = rr / (1+rr)
-
- if rand() < pi_eta
- dpm.aa = rand(Distributions.Gamma(dpm.a1+dpm.K, 1/(dpm.a2-log(eta))))
- else
- dpm.aa = rand(Distributions.Gamma(dpm.a1+dpm.K-1, 1/(dpm.a2-log(eta))))
- end
- end
- end
-
- nn_string = nn2string(nn)
- if !haskey(states, nn_string)
- n_states += 1
- states[nn_string] = n_states
- end
- dpm.K_hist[iteration] = states[nn_string]
- dpm.K_zz_dict[states[nn_string]] = deepcopy(zz)
- elapsed_time = toq()
- end
- return states
- end
-
-
- function posterior{T1, T2}(dpm::DPM{T1}, xx::Vector{T2}, K::Int64, K_truncation::Int64=0)
2 n_components = 0
1 if K_truncation == 0
1 n_components = K
- else
0 n_components = K_truncation
- end
-
1 bayesian_components = [deepcopy(dpm.bayesian_component) for kk=1:n_components]
1 zz = dpm.K_zz_dict[K]
-
1 NN = length(xx)
1 nn = zeros(Int64, n_components)
-
1 for ii = 1:NN
5000 kk = zz[ii]
5000 additem(bayesian_components[kk], xx[ii])
5000 nn[kk] += 1
- end
-
1 return([posterior(bayesian_components[kk]) for kk=1:n_components], nn)
- end
-
And here is the memory allocation:
- #=
- DPM
-
- Dirichlet Process Mixture Models
-
- 25/08/2015
- Adham Beyki, odinay#gmail.com
-
- =#
-
- type DPM{T}
- bayesian_component::T
- K::Int64
- aa::Float64
- a1::Float64
- a2::Float64
- K_hist::Vector{Int64}
- K_zz_dict::Dict{Int64, Vector{Int64}}
-
- DPM{T}(c::T, K::Int64, aa::Float64, a1::Float64, a2::Float64) = new(c, K, aa, a1, a2,
- Int64[], (Int64 => Vector{Int64})[])
- end
0 DPM{T}(c::T, K::Int64, aa::Real, a1::Real, a2::Real) = DPM{typeof(c)}(c, K, convert(Float64, aa),
- convert(Float64, a1), convert(Float64, a2))
-
- function Base.show(io::IO, dpm::DPM)
- println(io, "Dirichlet Mixture Model with $(dpm.K) $(typeof(dpm.bayesian_component)) components")
- end
-
- function initialize_gibbs_sampler!(dpm::DPM, zz::Vector{Int64})
- # populates the cluster labels randomly
0 zz[:] = rand(1:dpm.K, length(zz))
- end
-
- function DPM_sample_hyperparam(aa::Float64, a1::Float64, a2::Float64, K::Int64, NN::Int64, iters::Int64)
-
- # resampling concentration parameter based on Escobar and West 1995
0 for n = 1:iters
0 eta = rand(Distributions.Beta(aa+1, NN))
0 rr = (a1+K-1) / (NN*(a2-log(NN)))
0 pi_eta = rr / (1+rr)
-
0 if rand() < pi_eta
0 aa = rand(Distributions.Gamma(a1+K, 1/(a2-log(eta))))
- else
0 aa = rand(Distributions.Gamma(a1+K-1, 1/(a2-log(eta))))
- end
- end
0 aa
- end
-
- function DPM_sample_pp{T1, T2}(
- bayesian_components::Vector{T1},
- xx::T2,
- nn::Vector{Float64},
- pp::Vector{Float64},
- aa::Float64)
-
0 K = length(nn)
0 #inbounds for kk = 1:K
0 pp[kk] = log(nn[kk]) + logpredictive(bayesian_components[kk], xx)
- end
0 pp[K+1] = log(aa) + logpredictive(bayesian_components[K+1], xx)
0 normalize_pp!(pp, K+1)
0 return sample(pp[1:K+1])
- end
-
-
- function collapsed_gibbs_sampler!{T1, T2}(
- dpm::DPM{T1},
- xx::Vector{T2},
- zz::Vector{Int64},
- n_burnins::Int64, n_lags::Int64, n_samples::Int64, n_internals::Int64; max_clusters::Int64=100)
-
-
191688 NN = length(xx) # number of data points
96 nn = zeros(Float64, dpm.K) # count array
0 n_iterations = n_burnins + (n_samples)*(n_lags+1)
384 bayesian_components = [deepcopy(dpm.bayesian_component) for k = 1:dpm.K+1]
2864 dpm.K_hist = zeros(Int64, n_iterations)
176 pp = zeros(Float64, max_clusters)
-
48 tic()
0 for ii = 1:NN
0 kk = zz[ii]
0 additem(bayesian_components[kk], xx[ii])
0 nn[kk] += 1
- end
0 dpm.K_hist[1] = dpm.K
0 elapsed_time = toq()
-
0 for iteration = 1:n_iterations
-
5329296 println("iteration: $iteration, KK: $(dpm.K), KK mode: $(indmax(hist(dpm.K_hist,
- 0.5:maximum(dpm.K_hist)+0.5)[2])), elapsed time: $elapsed_time")
-
16800 tic()
28000000 #inbounds for ii = 1:NN
0 kk = zz[ii]
0 nn[kk] -= 1
0 delitem(bayesian_components[kk], xx[ii])
-
- # remove the cluster if empty
0 if nn[kk] == 0
161880 println("\tcomponent $kk has become inactive")
0 splice!(nn, kk)
0 splice!(bayesian_components, kk)
0 dpm.K -= 1
-
- # shifting the labels one cluster back
69032 idx = find(x -> x>kk, zz)
42944 zz[idx] -= 1
- end
-
0 kk = DPM_sample_pp(bayesian_components, xx[ii], nn, pp, dpm.aa)
-
0 if kk == dpm.K+1
158976 println("\tcomponent $kk activated.")
14144 push!(bayesian_components, deepcopy(dpm.bayesian_component))
4872 push!(nn, 0)
0 dpm.K += 1
- end
-
0 zz[ii] = kk
0 nn[kk] += 1
0 additem(bayesian_components[kk], xx[ii])
- end
-
0 dpm.aa = DPM_sample_hyperparam(dpm.aa, dpm.a1, dpm.a2, dpm.K, NN, n_internals)
0 dpm.K_hist[iteration] = dpm.K
14140000 dpm.K_zz_dict[dpm.K] = deepcopy(zz)
0 elapsed_time = toq()
- end
- end
-
- function truncated_gibbs_sampler{T1, T2}(dpm::DPM{T1}, xx::Vector{T2}, zz::Vector{Int64},
- n_burnins::Int64, n_lags::Int64, n_samples::Int64, n_internals::Int64, K_truncation::Int64)
-
- NN = length(xx) # number of data points
- nn = zeros(Int64, K_truncation) # count array
- bayesian_components = [deepcopy(dpm.bayesian_component) for k = 1:K_truncation]
- n_iterations = n_burnins + (n_samples)*(n_lags+1)
- dpm.K_hist = zeros(Int64, n_iterations)
- states = (ASCIIString => Int64)[]
- n_states = 0
-
- tic()
- for ii = 1:NN
- kk = zz[ii]
- additem(bayesian_components[kk], xx[ii])
- nn[kk] += 1
- end
- dpm.K_hist[1] = dpm.K
-
- # constructing the sticks
- beta_VV = rand(Distributions.Beta(1.0, dpm.aa), K_truncation)
- beta_VV[end] = 1.0
- π = ones(Float64, K_truncation)
- π[2:end] = 1 - beta_VV[1:K_truncation-1]
- π = log(beta_VV) + log(cumprod(π))
-
- elapsed_time = toq()
-
- for iteration = 1:n_iterations
-
- println("iteration: $iteration, # active components: $(length(findn(nn)[1])), mode: $(indmax(hist(dpm.K_hist,
- 0.5:maximum(dpm.K_hist)+0.5)[2])), elapsed time: $elapsed_time \n", nn)
-
- tic()
- for ii = 1:NN
- kk = zz[ii]
- nn[kk] -= 1
- delitem(bayesian_components[kk], xx[ii])
-
- # resampling label
- pp = zeros(Float64, K_truncation)
- for kk = 1:K_truncation
- pp[kk] = π[kk] + logpredictive(bayesian_components[kk], xx[ii])
- end
- pp = exp(pp - maximum(pp))
- pp /= sum(pp)
-
- # sample from pp
- kk = sampleindex(pp)
- zz[ii] = kk
- nn[kk] += 1
- additem(bayesian_components[kk], xx[ii])
-
- for kk = 1:K_truncation-1
- gamma1 = 1 + nn[kk]
- gamma2 = dpm.aa + sum(nn[kk+1:end])
- beta_VV[kk] = rand(Distributions.Beta(gamma1, gamma2))
- end
- beta_VV[end] = 1.0
- π = ones(Float64, K_truncation)
- π[2:end] = 1 - beta_VV[1:K_truncation-1]
- π = log(beta_VV) + log(cumprod(π))
-
- # resampling concentration parameter based on Escobar and West 1995
- for internal_iters = 1:n_internals
- eta = rand(Distributions.Beta(dpm.aa+1, NN))
- rr = (dpm.a1+dpm.K-1) / (NN*(dpm.a2-log(NN)))
- pi_eta = rr / (1+rr)
-
- if rand() < pi_eta
- dpm.aa = rand(Distributions.Gamma(dpm.a1+dpm.K, 1/(dpm.a2-log(eta))))
- else
- dpm.aa = rand(Distributions.Gamma(dpm.a1+dpm.K-1, 1/(dpm.a2-log(eta))))
- end
- end
- end
-
- nn_string = nn2string(nn)
- if !haskey(states, nn_string)
- n_states += 1
- states[nn_string] = n_states
- end
- dpm.K_hist[iteration] = states[nn_string]
- dpm.K_zz_dict[states[nn_string]] = deepcopy(zz)
- elapsed_time = toq()
- end
- return states
- end
-
-
- function posterior{T1, T2}(dpm::DPM{T1}, xx::Vector{T2}, K::Int64, K_truncation::Int64=0)
0 n_components = 0
0 if K_truncation == 0
0 n_components = K
- else
0 n_components = K_truncation
- end
-
0 bayesian_components = [deepcopy(dpm.bayesian_component) for kk=1:n_components]
0 zz = dpm.K_zz_dict[K]
-
0 NN = length(xx)
0 nn = zeros(Int64, n_components)
-
0 for ii = 1:NN
0 kk = zz[ii]
0 additem(bayesian_components[kk], xx[ii])
0 nn[kk] += 1
- end
-
0 return([posterior(bayesian_components[kk]) for kk=1:n_components], nn)
- end
-
What I don't seem to understand is why for instance a simple assignment which only runs twice, allocates 191688 units (I assume the unit is Bytes, I am not sure though).
.cov:
2 NN = length(xx) # number of data points
.mem:
191688 NN = length(xx) # number of data points
or this one is worse:
cov:
352 #inbounds for ii = 1:NN
mem:
28000000 #inbounds for ii = 1:NN
The answer is briefly mentioned in the docs, "Under the user setting, the first line of any function directly called from the REPL will exhibit allocation due to events that happen in the REPL code itself." Also possibly relevant: "More significantly, JIT-compilation also adds to allocation counts, because much of Julia’s compiler is written in Julia (and compilation usually requires memory allocation). The recommended procedure is to force compilation by executing all the commands you want to analyze, then call Profile.clear_malloc_data() to reset all allocation counters."
The bottom line: that first line is being blamed for allocations that happen elsewhere, because it's the first line to start reporting allocation again.
Related
I'm learning to program and I wanted help to solve it, in the code it says that the variable "game" is not associated with anything
-- title: jogo teste -- author: joao -- desc: n interessa -- site: fds -- version: 0.1 -- engine: tic-80 --Variable game { whidth = 240, height = 136 } hero { x = game.whidth / 2, y = game.height / 2 } function TIC() cls() if btn(0) and hero.y > 0 then hero.y = hero.y - 1 end if btn(1) and game.height then hero.y = hero.y + 1 end if btn(2) then hero.x = hero.x - 1 end if btn(3) then hero.x = hero.x + 1 end rect(game.x,game.y,10,10,11) end
math library is missing in the latest update of Logitech G-Hub
local delay = math.random(25, 50) [string "LuaVM"]:5: attempt to index a nil value (global 'math') I can't use math.random anymore is there any way to fix this ?
If math library is missed you can insert the following code block at the beginning of your script. It will not fix the whole math library, but only some of the most frequently used functions (including math.random). It will also fix the following errors: bad argument #1 to 'Sleep' (number has no integer representation) attempt to call a nil value (field 'getn') do local state_8, state_45, cached_bits, cached_bits_qty = 2, 0, 0, 0 local prev_width, prev_bits_in_factor, prev_k = 0 for c in GetDate():gmatch"." do state_45 = state_45 % 65537 * 23456 + c:byte() end local function get_53_random_bits() local value53 = 0 for shift = 26, 27 do local p = 2^shift state_45 = (state_45 * 233 + 7161722017421) % 35184372088832 repeat state_8 = state_8 * 76 % 257 until state_8 ~= 1 local r = state_8 % 32 local n = state_45 / 2^(13 - (state_8 - r) / 32) n = (n - n%1) % 2^32 / 2^r value53 = value53 * p + ((n%1 * 2^32) + (n - n%1)) % p end return value53 end for j = 1, 10 do get_53_random_bits() end local function get_random_bits(number_of_bits) local pwr_number_of_bits = 2^number_of_bits local result if number_of_bits <= cached_bits_qty then result = cached_bits % pwr_number_of_bits cached_bits = (cached_bits - result) / pwr_number_of_bits else local new_bits = get_53_random_bits() result = new_bits % pwr_number_of_bits cached_bits = (new_bits - result) / pwr_number_of_bits * 2^cached_bits_qty + cached_bits cached_bits_qty = 53 + cached_bits_qty end cached_bits_qty = cached_bits_qty - number_of_bits return result end table = table or {} table.getn = table.getn or function(x) return #x end math = math or {} math.huge = math.huge or 1/0 math.abs = math.abs or function(x) return x < 0 and -x or x end math.floor = math.floor or function(x) return x - x%1 end math.ceil = math.ceil or function(x) return x + (-x)%1 end math.min = math.min or function(x, y) return x < y and x or y end math.max = math.max or function(x, y) return x > y and x or y end math.sqrt = math.sqrt or function(x) return x^0.5 end math.pow = math.pow or function(x, y) return x^y end math.frexp = math.frexp or function(x) local e = 0 if x == 0 then return x, e end local sign = x < 0 and -1 or 1 x = x * sign while x >= 1 do x = x / 2 e = e + 1 end while x < 0.5 do x = x * 2 e = e - 1 end return x * sign, e end math.exp = math.exp or function(x) local e, t, k, p = 0, 1, 1 repeat e, t, k, p = e + t, t * x / k, k + 1, e until e == p return e end math.log = math.log or function(x) assert(x > 0) local a, b, c, d, e, f = x < 1 and x or 1/x, 0, 0, 1, 1 repeat repeat c, d, e, f = c + d, b * d / e, e + 1, c until c == f b, c, d, e, f = b + 1 - a * c, 0, 1, 1, b until b <= f return a == x and -f or f end math.log10 = math.log10 or function(x) return math.log(x) / 2.3025850929940459 end math.random = math.random or function(m, n) if m then if not n then m, n = 1, m end local k = n - m + 1 if k < 1 or k > 2^53 then error("Invalid arguments for function 'random()'", 2) end local width, bits_in_factor, modk if k == prev_k then width, bits_in_factor = prev_width, prev_bits_in_factor else local pwr_prev_width = 2^prev_width if k > pwr_prev_width / 2 and k <= pwr_prev_width then width = prev_width else width = 53 local width_low = -1 repeat local w = (width_low + width) / 2 w = w - w%1 if k <= 2^w then width = w else width_low = w end until width - width_low == 1 prev_width = width end bits_in_factor = 0 local bits_in_factor_high = width + 1 while bits_in_factor_high - bits_in_factor > 1 do local bits_in_new_factor = (bits_in_factor + bits_in_factor_high) / 2 bits_in_new_factor = bits_in_new_factor - bits_in_new_factor%1 if k % 2^bits_in_new_factor == 0 then bits_in_factor = bits_in_new_factor else bits_in_factor_high = bits_in_new_factor end end prev_k, prev_bits_in_factor = k, bits_in_factor end local factor, saved_bits, saved_bits_qty, pwr_saved_bits_qty = 2^bits_in_factor, 0, 0, 2^0 k = k / factor width = width - bits_in_factor local pwr_width = 2^width local gap = pwr_width - k repeat modk = get_random_bits(width - saved_bits_qty) * pwr_saved_bits_qty + saved_bits local modk_in_range = modk < k if not modk_in_range then local interval = gap saved_bits = modk - k saved_bits_qty = width - 1 pwr_saved_bits_qty = pwr_width / 2 repeat saved_bits_qty = saved_bits_qty - 1 pwr_saved_bits_qty = pwr_saved_bits_qty / 2 if pwr_saved_bits_qty <= interval then if saved_bits < pwr_saved_bits_qty then interval = nil else interval = interval - pwr_saved_bits_qty saved_bits = saved_bits - pwr_saved_bits_qty end end until not interval end until modk_in_range return m + modk * factor + get_random_bits(bits_in_factor) else return get_53_random_bits() / 2^53 end end local orig_Sleep = Sleep function Sleep(x) return orig_Sleep(x - x%1) end end
Spline with multiple durations
I'm trying to make a spline that has multiple durations. Basically, I want to create a spline generated through user supplied key frames. There may be multiple key frames with different durations. So I ask, how would I make a that travels at different speeds through different key frames. Let's say I want there to be 1 second between key frame A and B, but 5 seconds between B and C. Similar to the way animation editors like Source Filmmaker, or Autodesk Maya do it. Every time I've seen someone make a spline, it always has one, constant speed. Always X seconds between key frames, but this is not how animation editors work, as they have different speeds, that's what I want. Just to note, I have tried. I made the duration change once it gets to a different key frame, but that just instantly slows it down, like some slow motion movie effect, which is not what I'm looking for. Do I just gradually transition the speed to fit that of the next key frame? Is there just some equation? function smooth( points, steps ) --points is an array, steps is how many frames inbetween spline points if #points < 3 then return points end local steps = steps or 5 local spline = {} local count = #points - 1 local p0, p1, p2, p3, x, y, z for i = 1, count do if i == 1 then p0, p1, p2, p3 = points[i], points[i], points[i + 1], points[i + 2] elseif i == count then p0, p1, p2, p3 = points[#points - 2], points[#points - 1], points[#points], points[#points] else p0, p1, p2, p3 = points[i - 1], points[i], points[i + 1], points[i + 2] end for t = 0, 1, 1 / steps do -- Main spline equation x = 1 * ( ( 2 * p1.x ) + ( p2.x - p0.x ) * t + ( 2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x ) * t * t + ( 3 * p1.x - p0.x - 3 * p2.x + p3.x ) * t * t * t ) y = 1 * ( ( 2 * p1.y ) + ( p2.y - p0.y ) * t + ( 2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y ) * t * t + ( 3 * p1.y - p0.y - 3 * p2.y + p3.y ) * t * t * t ) z = 1 * ( ( 2 * p1.z ) + ( p2.z - p0.z ) * t + ( 2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z ) * t * t + ( 3 * p1.z - p0.z - 3 * p2.z + p3.z ) * t * t * t ) if not(#spline > 0 and spline[#spline].x == x and spline[#spline].y == y and spline[#spline].z == z) then table.insert( spline , { x = x , y = y, z = z } ) end end end return spline end
Straightforward approach was used: local zero_vector = {0, 0, 0} local function get_slope(is_edge, left, right) if is_edge then return zero_vector else local t = right.time - left.time assert(t > 0, "Non-increasing time sequence") return {(right[1] - left[1])/t, (right[2] - left[2])/t, (right[3] - left[3])/t} end end function smooth(checkpoints, frames_per_second) frames_per_second = frames_per_second or 5 if #checkpoints < 2 then return checkpoints end -- Prepare formulas for each segment of spline local formulas = {} for segment = 1, #checkpoints - 1 do local left = checkpoints[segment] local right = checkpoints[segment + 1] local t = right.time - left.time assert(t > 0, "Non-increasing time sequence") local left_deriv = get_slope(segment == 1, checkpoints[segment - 1], right) local right_deriv = get_slope(segment == #checkpoints - 1, left, checkpoints[segment + 2]) formulas[segment] = {} for j = 1, 3 do local d = left[j] local c = left_deriv[j] local a = (right[j] - d - c*t) / (t*t) local b = 3*a + (c - right_deriv[j])/t formulas[segment][j] = {(a - b)/t, b, c, d} end end -- Calculate spline points local total_seconds = checkpoints[#checkpoints].time - checkpoints[1].time local segment = 1 local spline = {} for frame_no = 0, total_seconds * frames_per_second do local t = checkpoints[1].time + frame_no / frames_per_second local point = {time = t} while segment < #formulas and t > checkpoints[segment + 1].time do segment = segment + 1 end t = t - checkpoints[segment].time for j = 1, 3 do local c = formulas[segment][j] point[j] = ((c[1]*t + c[2])*t + c[3])*t + c[4] end table.insert(spline, point) end return spline end Usage example: -- x y z "timestamp in seconds" local checkpoint_A = {11, 12, 13, time = 0} local checkpoint_B = {21, 15, 18, time = 1} -- 1 second between A and B local checkpoint_C = {13, 24, 20, time = 6} -- 5 seconds between B and C local checkpoints = {checkpoint_A, checkpoint_B, checkpoint_C} -- total duration is 6 seconds, 10 frames per second, 61 points returned local array_of_61_points = smooth(checkpoints, 10) for _, point in ipairs(array_of_61_points) do print(string.format("time = %.1f, x = %.3f, y = %.3f, z = %.3f", point.time, point[1], point[2], point[3])) end
Flawed game logic in Lua code
The code is for a simple snake clone and I don't want to let the snake me able to go left if it's going right already. It works if I just press LEFT when going RIGHT but if I for example presses UP then LEFT within the time frame it starts to move left. function self.update(dt) if love.keyboard.isDown(self.left) and self.prevvelocity.x ~= 1 then self.velocity.x = -1 self.velocity.y = 0 end if love.keyboard.isDown(self.right) and self.prevvelocity.x ~= -1 then self.velocity.x = 1 self.velocity.y = 0 end if love.keyboard.isDown(self.up) and self.prevvelocity.y ~= 1 then self.velocity.x = 0 self.velocity.y = -1 end if love.keyboard.isDown(self.down) and self.prevvelocity.y ~= -1 then self.velocity.x = 0 self.velocity.y = 1 end if self.timeSinceLastMove < self.speedinverted then self.timeSinceLastMove = self.timeSinceLastMove + dt else table.remove(self.tail, 1) tail = { x = self.position.x, y = self.position.y } table.insert(self.tail, tail) self.position.x = self.position.x + self.velocity.x * tileSize self.position.y = self.position.y + self.velocity.y * tileSize self.prevvelocity = self.velocity self.timeSinceLastMove = 0; end end
function self.update(dt) if love.keyboard.isDown(self.left) and self.prevvelocity.x ~= 1 then self.velocity.x = -1 self.velocity.y = 0 end if love.keyboard.isDown(self.right) and self.prevvelocity.x ~= -1 then self.velocity.x = 1 self.velocity.y = 0 end if love.keyboard.isDown(self.up) and self.prevvelocity.y ~= 1 then self.velocity.x = 0 self.velocity.y = -1 end if love.keyboard.isDown(self.down) and self.prevvelocity.y ~= -1 then self.velocity.x = 0 self.velocity.y = 1 end self.timeSinceLastMove = self.timeSinceLastMove + dt if self.timeSinceLastMove >= self.speedinverted then self.timeSinceLastMove = self.timeSinceLastMove - self.speedinverted self.position.x = self.position.x + self.velocity.x * tileSize self.position.y = self.position.y + self.velocity.y * tileSize table.remove(self.tail, 1) local head = { x = self.position.x, y = self.position.y } table.insert(self.tail, head) self.prevvelocity = { x = self.velocity.x, y = self.velocity.y } end end
How do I get a unknown float value based on y position of UIButton
I have an equation to calculate a float value based on the y postion of a UIButton: float mainVolume = 1 - ((button.y - 126) / 139); How to i calculate a float value if I have the previous mainVolume float value is already set? float newButtonYposition = ? 0.568345 = 1 - ((newButtonYposition.y - 126) / 139);
Apply the math rules: // Start with this line; rename newButtonYposition.y as X for brevity 0.568345 = 1 - ((X - 126) / 139) // Subtract one from both sides 0.568345 - 1 = - ((X - 126) / 139) // Change sign 1 - 0.568345 = (X - 126) / 139 // Multiply by 139 139 * (1 - 0.568345) = X - 126 // Add 126 139 * (1 - 0.568345) + 126 = X Calculating the expression produces newButtonYposition.y = 186