Linear zoom function 2D - math

I have a canvas and a scale value. The maximum scale is 1, the minimum scale value is something like 0.1 or above.
Let’s say we have discrete time units. I’m looking for a function that zooms linear over an time interval I (lets say 100 time units), from a start zoom s to an end zoom e. Let 0 >= i < I be the current interval.
Example: Zoom from 0.2 to 1.0 in 100 time units.
Obviously zoom(i) = (e-s)/I * i does not produce a linear zoom. Because a step from 0.2 to 0.4 doubles the zoom, while the same amount from 0.8 to 1.0 only increases the zoom by 25%.
I was thinking that this function needs something logarithmic to base 2, but I’m stuck finding the right function.

To provide constant ratio with constant argument difference, you need exponential function (it is possible to use any base, e, 2, 10 and so on with corresponding logarithms)
F(x) = A * Exp(B * x)
To get coefficients A and B for given border conditions (argument x0 corresponds to function value F0):
F0 = A * Exp(B * x0)
F1 = A * Exp(B * x1)
divide the second equation by the first:
Exp(B * (x1 -x0) = F1 / F0
B * (x1 -x0) = ln(F1 / F0)
so
B = ln(F1 / F0) / (x1 - x0)
and
A = F0 * Exp(-B * x0)
For your example
x0=0, x1=100
zoom0 = 0.2, zoom1=1
B = ln(5) / 100 = 0.0161
A = 0.2 * Exp(0) = 0.2
zoom(i) = 0.2 * Exp(0.0161 * i)
zoom(0) = 0.2
zoom(50) = 0.447
zoom(100) = 1
note that
zoom(50) / zoom(0) = zoom(100) / zoom(50)

What you need is not a logarithm but root. You requirements is effectively following: you want to find such sequence A[i] that
A[0] = 0.1
A[N] = 1
A[i+1]/A[i] = k, where kis some constant
The solution for this is obviously
A[i] = 0.1 * k^i
and so k should be
k^N = 1/0.1 = 10
or
k = root(10, N) = 10^(1/N)
For practical reasons it might be better to use N which is power of 2 so you can calculate some intermediate results by multiplying by a lesser root to have less
rounding error accumulated. What I means is that
a[N/2] = sqrt(0.1) = 0.1 * sqrt(1/0.1)
a[N/4] = 0.1 * root(1/0.1, 4)
a[3*N/4] = a[N/2] * root(1/0.1, 4)
It also might make sense to change starting value of 0.1 to something that is itself some power for example 1/9 or 1/16

Related

Linear combinations of Zygote.Grads

I am building and training a neural network model with Flux, and I am wondering if there is a way to take linear combinations of Zygote.Grads types.
Here is a minimalistic example. This is how it is typically done:
m = hcat(2.0); b = hcat(-1.0); # random 1 x 1 matrices
f(x) = m*x .+ b
ps = Flux.params(m, b) # parameters to be adjusted
inputs = [0.3 1.5] # random 1 x 2 matrix
loss(x) = sum( f(x).^2 )
gs = Flux.gradient(() -> loss(inputs), ps) # the typical way
#show gs[m], gs[b] # 5.76, 3.2
But I want to do the same calculation by computing gradients at a deeper level, and then assembling it at the end. For example:
input1 = hcat(inputs[1, 1]); input2 = hcat(inputs[1, 2]); # turn each input into a 1 x 1 matrix
grad1 = Flux.gradient(() -> f(input1)[1], ps) # df/dp using input1 (where p is m or b)
grad2 = Flux.gradient(() -> f(input2)[1], ps) # df/dp using input2 (where p is m or b)
predicted1 = f(input1)[1]
predicted2 = f(input2)[1]
myGrad_m = (2 * predicted1 * grad1[m]) + (2 * predicted2 * grad2[m]) # 5.76
myGrad_b = (2 * predicted1 * grad1[b]) + (2 * predicted2 * grad2[b]) # 3.2
Above, I used the chain rule and linearity of the derivative to decompose the gradient of the loss() function:
d(loss)/dp = d( sum(f^2) ) / dp = sum( d(f^2)/dp ) = sum( 2*f * df/dp )
Then, I calculated df/dp using Zygote.gradient, and then combined the results at the end.
But notice that I had to combine m and b separately. This was fine because there were only 2 parameters.
However, if there were a 1000 parameters, I would want to do something like this, which is a linear combination of the Zygote.Grads:
myGrad = (2 * predicted1 * grad1) + (2 * predicted2 * grad2)
But, I get an error saying that the + and * operators are not defined for these types. How can I get this shortcut to work?
Just turn each */+ into .*/.+ (i.e. use broadcasting) or you can use map to apply a function to multiple Grads at once. This is described in the Zygote docs here. Note that in order for this to work, all the Grads must share the same keys (so they must correspond to the same parameters).

Lagrange Multiplier Method using NLsolve.jl

I would like to minimize a distance function ||dz - z|| under the constraint that g(z) = 0.
I wanted to use Lagrange Multipliers to solve this problem. Then I used NLsolve.jl to solve the non-linear equation that I end up with.
using NLsolve
using ForwardDiff
function ProjLagrange(dz, g::Function)
λ_init = ones(size(g(dz...),1))
initial_x = vcat(dz, λ_init)
function gradL!(F, x)
len_dz = length(dz)
z = x[1:len_dz]
λ = x[len_dz+1:end]
F = Array{Float64}(undef, length(x))
my_distance(z) = norm(dz - z)
∇f = z -> ForwardDiff.gradient(my_distance, z)
F[1:len_dz] = ∇f(z) .- dot(λ, g(z...))
if length(λ) == 1
F[end] = g(z...)
else
F[len_dz+1:end] = g(z)
end
end
nlsolve(gradL!, initial_x)
end
g_test(x1, x2, x3) = x1^2 + x2 - x2 + 5
z = [1000,1,1]
ProjLagrange(z, g_test)
But I always end up with Zero: [NaN, NaN, NaN, NaN] and Convergence: false.
Just so you know I have already solved the equation by using Optim.jl and minimizing the following function: Proj(z) = b * sum(abs.(g(z))) + a * norm(dz - z).
But I would really like to know if this is possible with NLsolve. Any help is greatly appreciated!
Starting almost from scratch and wikipedia's Lagrange multiplier page because it was good for me, the code below seemed to work. I added an λ₀s argument to the ProjLagrange function so that it can accept a vector of initial multiplier λ values (I saw you initialized them at 1.0 but I thought this was more generic). (Note this has not been optimized for performance!)
using NLsolve, ForwardDiff, LinearAlgebra
function ProjLagrange(x₀, λ₀s, gs, n_it)
# distance function from x₀ and its gradients
f(x) = norm(x - x₀)
∇f(x) = ForwardDiff.gradient(f, x)
# gradients of the constraints
∇gs = [x -> ForwardDiff.gradient(g, x) for g in gs]
# Form the auxiliary function and its gradients
ℒ(x,λs) = f(x) - sum(λ * g(x) for (λ,g) in zip(λs,gs))
∂ℒ∂x(x,λs) = ∇f(x) - sum(λ * ∇g(x) for (λ,∇g) in zip(λs,∇gs))
∂ℒ∂λ(x,λs) = [g(x) for g in gs]
# as a function of a single argument
nx = length(x₀)
ℒ(v) = ℒ(v[1:nx], v[nx+1:end])
∇ℒ(v) = vcat(∂ℒ∂x(v[1:nx], v[nx+1:end]), ∂ℒ∂λ(v[1:nx], v[nx+1:end]))
# and solve
v₀ = vcat(x₀, λ₀s)
nlsolve(∇ℒ, v₀, iterations=n_it)
end
# test
gs_test = [x -> x[1]^2 + x[2] - x[3] + 5]
λ₀s_test = [1.0]
x₀_test = [1000.0, 1.0, 1.0]
n_it = 100
res = ProjLagrange(x₀_test, λ₀s_test, gs_test, n_it)
gives me
julia> res = ProjLagrange(x₀_test, λ₀s_test, gs_test, n_it)
Results of Nonlinear Solver Algorithm
* Algorithm: Trust-region with dogleg and autoscaling
* Starting Point: [1000.0, 1.0, 1.0, 1.0]
* Zero: [9.800027199717013, -49.52026655749088, 51.520266557490885, -0.050887973682118504]
* Inf-norm of residuals: 0.000000
* Iterations: 10
* Convergence: true
* |x - x'| < 0.0e+00: false
* |f(x)| < 1.0e-08: true
* Function Calls (f): 11
* Jacobian Calls (df/dx): 11
I altered your code as below (see my comments in there) and got the following output. It doesn't throw NaNs anymore, reduces the objective and converges. Does this differ from your Optim.jl results?
Results of Nonlinear Solver Algorithm
* Algorithm: Trust-region with dogleg and autoscaling
* Starting Point: [1000.0, 1.0, 1.0, 1.0]
* Zero: [9.80003, -49.5203, 51.5203, -0.050888]
* Inf-norm of residuals: 0.000000
* Iterations: 10
* Convergence: true
* |x - x'| < 0.0e+00: false
* |f(x)| < 1.0e-08: true
* Function Calls (f): 11
* Jacobian Calls (df/dx): 11
using NLsolve
using ForwardDiff
using LinearAlgebra: norm, dot
using Plots
function ProjLagrange(dz, g::Function, n_it)
λ_init = ones(size(g(dz),1))
initial_x = vcat(dz, λ_init)
# These definitions can go outside as well
len_dz = length(dz)
my_distance = z -> norm(dz - z)
∇f = z -> ForwardDiff.gradient(my_distance, z)
# In fact, this is probably the most vital difference w.r.t. your proposal.
# We need the gradient of the constraints.
∇g = z -> ForwardDiff.gradient(g, z)
function gradL!(F, x)
z = x[1:len_dz]
λ = x[len_dz+1:end]
# `F` is memory allocated by NLsolve to store the residual of the
# respective call of `gradL!` and hence doesn't need to be allocated
# anew every time (or at all).
F[1:len_dz] = ∇f(z) .- λ .* ∇g(z)
F[len_dz+1:end] .= g(z)
end
return nlsolve(gradL!, initial_x, iterations=n_it, store_trace=true)
end
# Presumable here is something wrong: x2 - x2 is not very likely, also made it
# callable directly with an array argument
g_test = x -> x[1]^2 + x[2] - x[3] + 5
z = [1000,1,1]
n_it = 10000
res = ProjLagrange(z, g_test, n_it)
# Ugly reformatting here
trace = hcat([[state.iteration; state.fnorm; state.stepnorm] for state in res.trace.states]...)
plot(trace[1,:], trace[2,:], label="f(x) inf-norm", xlabel="steps")
Evolution of inf-norm of f(x) over iteration steps
[Edit: Adapted solution to incorporate correct gradient computation for g()]

Solving a system of differential equations in R

I have a simple flux model in R. It boils down to two differential equations that model two state variables within the model, we'll call them A and B. They are calculated as simple difference equations of four component fluxes flux1-flux4, 5 parameters p1-p5, and a 6th parameter, of_interest, that can take on values between 0-1.
parameters<- c(p1=0.028, p2=0.3, p3=0.5, p4=0.0002, p5=0.001, of_interest=0.1)
state <- c(A=28, B=1.4)
model<-function(t,state,parameters){
with(as.list(c(state,parameters)),{
#fluxes
flux1 = (1-of_interest) * p1*(B / (p2 + B))*p3
flux2 = p4* A #microbial death
flux3 = of_interest * p1*(B / (p2 + B))*p3
flux4 = p5* B
#differential equations of component fluxes
dAdt<- flux1 - flux2
dBdt<- flux3 - flux4
list(c(dAdt,dBdt))
})
I would like to write a function to take the derivative of dAdt with respect to of_interest, set the derived equation to 0, then rearrange and solve for the value of of_interest. This will be the value of the parameter of_interest that maximizes the function dAdt.
So far I have been able to solve the model at steady state, across the possible values of of_interest to demonstrate there should be a maximum.
require(rootSolve)
range<- seq(0,1,by=0.01)
for(i in range){
of_interest=i
parameters<- c(p1=0.028, p2=0.3, p3=0.5, p4=0.0002, p5=0.001, of_interest=of_interest)
state <- c(A=28, B=1.4)
ST<- stode(y=y,func=model,parms=parameters,pos=T)
out<- c(out,ST$y[1])
Then plotting:
plot(out~range, pch=16,col='purple')
lines(smooth.spline(out~range,spar=0.35), lwd=3,lty=1)
How can I analytically solve for the value of of_interest that maximizes dAdt in R? If an analytical solution is not possible, how can I know, and how can I go about solving this numerically?
Update: I think this problem can be solved with the deSolve package in R, linked here, however I am having trouble implementing it using my particular example.
Your equation in B(t) is just-about separable since you can divide out B(t), from which you can get that
B(t) = C * exp{-p5 * t} * (p2 + B(t)) ^ {of_interest * p1 * p3}
This is an implicit solution for B(t) which we'll solve point-wise.
You can solve for C given your initial value of B. I suppose t = 0 initially? In which case
C = B_0 / (p2 + B_0) ^ {of_interest * p1 * p3}
This also gives a somewhat nicer-looking expression for A(t):
dA(t) / dt = B_0 / (p2 + B_0) * p1 * p3 * (1 - of_interest) *
exp{-p5 * t} * ((p2 + B(t) / (p2 + B_0)) ^
{of_interest * p1 * p3 - 1} - p4 * A(t)
This can be solved by integrating factor (= exp{p4 * t}), via numerical integration of the term involving B(t). We specify the lower limit of the integral as 0 so that we never have to evaluate B outside the range [0, t], which means the integrating constant is simply A_0 and thus:
A(t) = (A_0 + integral_0^t { f(tau; parameters) d tau}) * exp{-p4 * t}
The basic gist is B(t) is driving everything in this system -- the approach will be: solve for the behavior of B(t), then use this to figure out what's going on with A(t), then maximize.
First, the "outer" parameters; we also need nleqslv to get B:
library(nleqslv)
t_min <- 0
t_max <- 10000
t_N <- 10
#we'll only solve the behavior of A & B over t_rng
t_rng <- seq(t_min, t_max, length.out = t_N)
#I'm calling of_interest ttheta
ttheta_min <- 0
ttheta_max <- 1
ttheta_N <- 5
tthetas <- seq(ttheta_min, ttheta_max, length.out = ttheta_N)
B_0 <- 1.4
A_0 <- 28
#No sense storing this as a vector when we'll only ever use it as a list
parameters <- list(p1 = 0.028, p2 = 0.3, p3 = 0.5,
p4 = 0.0002, p5 = 0.001)
From here, the basic outline is:
Given the parameter values (in particular ttheta), solve for BB over t_rng via non-linear equation solving
Given BB and the parameter values, solve for AA over t_rng by numerical integration
Given AA and your expression for dAdt, plug & maximize.
derivs <-
sapply(tthetas, function(th){
#append current ttheta
params <- c(parameters, ttheta = th)
#declare a function we'll use to solve for B (see above)
b_slv <- function(b, t)
with(params, b - B_0 * ((p2 + b)/(p2 + B_0)) ^
(ttheta * p1 * p3) * exp(-p5 * t))
#solving point-wise (this is pretty fast)
# **See below for a note**
BB <- sapply(t_rng, function(t) nleqslv(B_0, function(b) b_slv(b, t))$x)
#this is f(tau; params) that I mentioned above;
# we have to do linear interpolation since the
# numerical integrator isn't constrained to the grid.
# **See below for note**
a_int <- function(t){
#approximate t to the grid (t_rng)
# (assumes B is monotonic, which seems to be true)
# (also, if t ends up negative, just assign t_rng[1])
t_n <- max(1L, which.max(t_rng - t >= 0) - 1L)
idx <- t_n:(t_n+1)
ts <- t_rng[idx]
#distance-weighted average of the local B values
B_app <- sum((-1) ^ (0:1) * (t - ts) / diff(ts) * BB[idx])
#finally, f(tau; params)
with(params, (1 - ttheta) * p1 * p3 * B_0 / (p2 + B_0) *
((p2 + B_app)/(p2 + B_0)) ^ (ttheta * p1 * p3 - 1) *
exp((p4 - p5) * t))
}
#a_int only works on scalars; the numeric integrator
# requires a version that works on vectors
a_int_v <- function(t) sapply(t, a_int)
AA <- exp(-params$p4 * t_rng) *
sapply(t_rng, function(tt)
#I found the subdivisions constraint binding in some cases
# at the default value; no trouble at 1000.
A_0 + integrate(a_int_v, 0, tt, subdivisions = 1000L)$value)
#using the explicit version of dAdt given as flux1 - flux2
max(with(params, (1 - ttheta) * p1 * p3 * BB / (p2 + BB) - p4 * AA))})
Finally, simply run `tthetas[which.max(derivs)]` to get the maximizer.
Note:
This code is not optimized for efficiency. There are a few places where there are some potential speed-ups:
probably faster to run the equation solver recursively, as it'll converge faster with better initial guesses -- using the previous value instead of the initial value is surely better
Will be faster to simply use Riemann sums to integrate; the tradeoff is in accuracy, but should be fine if you have a dense enough grid. One beauty of Riemann is you won't have to interpolate at all, and numerically they're simple linear algebra. I ran this with t_N == ttheta_N == 1000L and it ran within a few minutes.
Probably possible to vectorize a_int directly instead of just sapplying on it, which concomitant speed-up by more direct appeal to BLAS.
Loads of other small stuff. Pre-compute ttheta * p1 * p3 since it's re-used so much, etc.
I didn't bother including any of that stuff, though, because you're honestly probably better off porting this to a faster language -- Julia is my own pet favorite, but of course R speaks well with C++, C, Fortran, etc.

Quadratic Bezier Curve: Calculate t given x

Good day. I am using a Quadratic Bezier Curve with the following configurations:
Start Point P1 = (1, 2)
Anchor Point P2 = (1, 8)
End Point P3 = (10, 8)
I know that given a t, I know I can solve for x and y using the following equation:
t = 0.5; // given example value
x = (1 - t) * (1 - t) * P1.x + 2 * (1 - t) * t * P2.x + t * t * P3.x;
y = (1 - t) * (1 - t) * P1.y + 2 * (1 - t) * t * P2.y + t * t * P3.y;
where P1.x is the x coordinate of P1, and so on.
What I've tried now is that given an x value, I calculate for t using wolframalpha and then I plug that t in to the y equation and I get a my x and y point.
However, I want to automate finding t and then y. I have a formula to get x and y given a t. However, I don't have a formula to get t based on x. I'm a bit rusty with my algebra and expanding the first equation to isolate t doesn't look too easy.
Does anyone have a formula to get t based on x? My google search skills are failing me as of now.
I think it's also worth noting that my Bezier curve faces right.
Any help will be very much appreciated. Thanks.
problem is that what you want to solve is not function in general
for any t is just one (x,y) pair
but for any x there can be 0,1,2,+inf solutions of t
I would do this iteratively
you already can get any point p(t)=Bezier(t) so use iteration of t to minimize distance |p(t).x-x|
for(t=0.0,dt=0.1;t<=1.0;t+=dt)
find all local mins of d=|p(t).x-x|
so when d start rising again set dt*=-0.1 and stop if |dt|<1e-6 or any other threshold. Stop if t is out of interval <0,1> and remember the solution to some list. Restore original t,dt and reset the local min search variables
process all local mins
eliminate all that has bigger distance then some threshold/accuracy compute y and do what you need with the point ...
It is much slower then algebraic approach but you can use this for any curvature not just quadratic
Usually cubic curves are used and do this algebraically with them is a nightmare.
Look at your Bernstein polynomials B[i]; you have...
x = SUM_i ( B[i](t) * P[i].x )
...where...
B[0](t) = t^2 - 2*t + 1
B[1](t) = -2*t^2 + 2*t
B[2](t) = t^2
...so you can rearrange (assuming I did this right)...
0 = (P[0].x - 2*P[1].x + P[2].x) * t^2 + (-2*P[0].x + 2*P[1].x) * t + P[0].x - x
Now you should just be able to use the quadratic formula to find if the solutions for t exist (i.e., are real, not complex), and what they are.
import numpy as np
import matplotlib.pyplot as plt
#Control points
p0=(1000,2500); p1=(2000,-1500); p2=(5000,3000)
#x-coordinates to fit
xcoord = [1750., 2750., 3950.,4760., 4900.]
# t variable with as few points as needed, considering accuracy. I found 30 is good enough
t = np.linspace(0,1,30)
# calculate coordinates of quadratic Bezier curve
x = (1 - t) * (1 - t) * p0[0] + 2 * (1 - t) * t * p1[0] + t * t * p2[0];
y = (1 - t) * (1 - t) * p0[1] + 2 * (1 - t) * t * p1[1] + t * t * p2[1];
# find the closest points to each x-coordinate. Interpolate y-coordinate
ycoord=[]
for ind in xcoord:
for jnd in range(len(x[:-1])):
if ind >= x[jnd] and ind <= x[jnd+1]:
ytemp = (ind-x[jnd])*(y[jnd+1]-y[jnd])/(x[jnd+1]-x[jnd]) + y[jnd]
ycoord.append(ytemp)
plt.figure()
plt.xlim(0, 6000)
plt.ylim(-2000, 4000)
plt.plot(p0[0],p0[1],'kx', p1[0],p1[1],'kx', p2[0],p2[1],'kx')
plt.plot((p0[0],p1[0]),(p0[1],p1[1]),'k:', (p1[0],p2[0]),(p1[1],p2[1]),'k:')
plt.plot(x,y,'r', x, y, 'k:')
plt.plot(xcoord, ycoord, 'rs')
plt.show()

Exponential volume control with a specified midpoint

I have a slider that returns values from 0 to 100.
I am using this to control the gain of an oscillator.
When the slider is at 0, I would like the gain to be 0.0
When the slider is at 50, I would like the gain to be 0.1
When the slider is at 100, I would like the gain to be 0.5
So I need to find an equation to get a smooth curve which passes through all of these points.
I've got the following equation which gives an exponential curve and gets the start and end points correct, but I don't know how to force the curve through the middle point. Can anyone help?
function logSlider(position){
var minP = 0;
var maxP = 100;
var minV = Math.log(0.0001);
var maxV = Math.log(0.5);
var scale = (maxV - minV) / (maxP - minP);
return Math.exp(minV + scale*(position-minP));
}
Here's a derivation of the function. All it takes is some algebra.
Model and Constraints
We want an exponential function of the following form, that takes number between 0 and 1:
f(t) = a * bt + c
The function must satisfy these constraints you gave:
f(0) = 0 = a + c
f(1/2) = 0.1 = a * b1/2 + c
f(1) = 0.5 = a * b + c
Solving for a, b, c
Let z2 = b.
a + c = 0
a * z + c = 0.1
a * z * z + c = 0.5
a * z - a = 0.1
a * z * z - a = 0.5
a * z * z - a * z = 0.4
(a * z - a) * z = 0.4
0.1 * z = 0.4
z = 4
b = 16
a * 4 - a = 0.1
a = 0.1 / 3
c = -0.1 / 3
Solution
f(t) = (0.1 / 3) * (16t - 1)
Here's a plot.
If you want to pass in values between 0 and 100, simply divide by 100 first.
Try
y = 0.2*(x/100)^3 + 0.3*(x/100)^2
The general solution for points (50, y_1) and (100, y_2) is
y = 2 (x/100)^3 * ( y_2-4*y_1) + (x/100)^2 * ( 8*y_1 - y_2 )

Resources