A project I'm working on needs a function which mimics 3ds/gmax's quaternion addition. A test case of (quat 1 2 3 4)+(quat 3 5 7 9) should equal (quat 20 40 54 2). These quats are in xyzw.
So, I figure it's basic algebra, given the clean numbers. It's got to be something like this multiply function, since it doesn't involve sin/cos:
const quaternion &operator *=(const quaternion &q)
{
float x= v.x, y= v.y, z= v.z, sn= s*q.s - v*q.v;
v.x= y*q.v.z - z*q.v.y + s*q.v.x + x*q.s;
v.y= z*q.v.x - x*q.v.z + s*q.v.y + y*q.s;
v.z= x*q.v.y - y*q.v.x + s*q.v.z + z*q.s;
s= sn;
return *this;
}
source
But, I don't understand how sn= s*q.s - v*q.v is supposed to work. s is a float, v is vector. Multiply vectors and add to float?
I'm not even sure which terms of direction/rotation/orientation these values represent, but if the function satisfies the quat values above, it'll work.
Found it. Turns out to be known as multiplication. Addition is multiplication. Up is sideways. Not confusing at all :/
fn qAdd q1 q2 = (
x1=q1.x
y1=q1.y
z1=q1.z
w1=q1.w
x2=q2.x
y2=q2.y
z2=q2.z
w2=q2.w
W = (W1 * W2) - (X1 * X2) - (Y1 * Y2) - (Z1 * Z2)
X = (W1 * X2) + (X1 * W2) + (Y1 * Z2) - (Z1 * Y2)
Y = (W1 * Y2) + (Y1 * W2) + (Z1 * X2) - (X1 * Z2)
Z = (W1 * Z2) + (Z1 * W2) + (X1 * Y2) - (Y1 * X2)
return (quat x y z w)
)
Swapping q1 & q2 yields different results, quite neither like addition nor multiplication.
source
Related
I have a function with 4 inputs (x1, x2, x3, x4) and 2 outputs (y1, y2) using Tensorflow. I would like to specify the gradients, since I perform some non-autodiff operations inside the function.
I need to specify the derivatives of the outputs with respect to the inputs. We can see these derivatives as a Jacobian of size (2,4). Regarding this, I have 8 derivatives: dy1_dx1, dy1_dx2, dy1_dx3, dy1_dx4, dy2_dx1, dy2_dx2, dy2_dx3 and dy2_dx4.
However, the grad function used in this tf.custom.gradient needs to have the same length as the inputs, this is 4. So, I do not know how to express 8 derivatives using just 4 elements. I tried to include them as lists, but it gives the error. Here is a general code to reproduce the error:
import tensorflow as tf
#tf.custom_gradient
def bar(x1, x2, x3, x4):
def grad(dy1, dy2):
dy1_dx1 = x2**2 * x3**3 * x4**4 #360000
dy1_dx2 = x1 * 2*x2 * x3**3 * x4**4 #480000
dy1_dx3 = x1 * x2**2 * 3*x3**2 * x4**4 #540000
dy1_dx4 = x1 * x2**2 * x3**3 * 4*x4**3 #576000
dy2_dx1 = x2**2 + x3**3 + x4**4 #698
dy2_dx2 = x1 + 2*x2 + x3**3 + x4**4 #697
dy2_dx3 = x1 + x2**2 + 3*x3**2 + x4**4 #684
dy2_dx4 = x1 + x2**2 + x3**3 + 4*x4**3 #575
return [dy1_dx1, dy2_dx1], [dy1_dx2, dy2_dx2], [dy1_dx3, dy2_dx3], [dy1_dx4, dy2_dx4]
y1 = x1 * x2**2 * x3**3 * x4**4
y2 = x1 + x2**2 + x3**3 + x4**4
return [y1, y2], grad
x1 = tf.constant(2.0, dtype=tf.float32)
x2 = tf.constant(3.0, dtype=tf.float32)
x3 = tf.constant(4.0, dtype=tf.float32)
x4 = tf.constant(5.0, dtype=tf.float32)
with tf.GradientTape(persistent=True) as tape:
tape.watch(x1)
tape.watch(x2)
tape.watch(x3)
tape.watch(x4)
z = bar(x1, x2, x3, x4)
print(tape.gradient(z, x1)) #[dy1_dx1, dy2_dx1]
print(tape.gradient(z, x2)) #[dy1_dx2, dy2_dx2]
print(tape.gradient(z, x3)) #[dy1_dx3, dy2_dx3]
print(tape.gradient(z, x4)) #[dy1_dx4, dy2_dx4]
The error says: "custom_gradient function expected to return 4 gradients, but returned 8 instead".
I expect someway to specify the correspondent 8 derivatives. Thank you in advance!
import numpy as np
import time
features, labels = d2l.get_data_ch7()
def init_adam_states():
v_w, v_b = torch.zeros((features.shape[1], 1),dtype=torch.float32), torch.zeros(1, dtype=torch.float32)
s_w, s_b = torch.zeros((features.shape[1], 1),dtype=torch.float32), torch.zeros(1, dtype=torch.float32)
return ((v_w, s_w), (v_b, s_b))
def adam(params, states, hyperparams):
beta1, beta2, eps = 0.9, 0.999, 1e-6
for p, (v, s) in zip(params, states):
v[:] = beta1 * v + (1 - beta1) * p.grad.data
s = beta2 * s + (1 - beta2) * p.grad.data**2
v_bias_corr = v / (1 - beta1 ** hyperparams['t'])
s_bias_corr = s / (1 - beta2 ** hyperparams['t'])
p.data -= hyperparams['lr'] * v_bias_corr / (torch.sqrt(s_bias_corr) + eps)
hyperparams['t'] += 1
def train_ch7(optimizer_fn, states, hyperparams, features, labels, batch_size=10, num_epochs=2):
# 初始化模型
net, loss = d2l.linreg, d2l.squared_loss
w = torch.nn.Parameter(torch.tensor(np.random.normal(0, 0.01, size=(features.shape[1], 1)), dtype=torch.float32),
requires_grad=True)
b = torch.nn.Parameter(torch.zeros(1, dtype=torch.float32), requires_grad=True)
def eval_loss():
return loss(net(features, w, b), labels).mean().item()
ls = [eval_loss()]
data_iter = torch.utils.data.DataLoader(torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)
for _ in range(num_epochs):
start = time.time()
print(w)
print(b)
for batch_i, (X, y) in enumerate(data_iter):
l = loss(net(X, w, b), y).mean() # 使⽤平均损失
# 梯度清零
if w.grad is not None:
w.grad.data.zero_()
b.grad.data.zero_()
l.backward()
optimizer_fn([w, b], states, hyperparams) # 迭代模型参数
if (batch_i + 1) * batch_size % 100 == 0:
ls.append(eval_loss()) # 每100个样本记录下当前训练误差
# 打印结果和作图
print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
d2l.set_figsize()
d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
d2l.plt.xlabel('epoch')
d2l.plt.ylabel('loss')
train_ch7(adam, init_adam_states(), {'lr': 0.01, 't': 1}, features, labels)
I want to implement the Adam algorithm in the follow code and I feel confused in the function named adam.
v = beta1 * v + (1 - beta1) * p.grad.data
s = beta2 * s + (1 - beta2) * p.grad.data**2
when I use the follow code, the loss function curve is figure 1.
figure 1
v[:] = beta1 * v + (1 - beta1) * p.grad.data
s = beta2 * s + (1 - beta2) * p.grad.data**2
or
v = beta1 * v + (1 - beta1) * p.grad.data
s[:] = beta2 * s + (1 - beta2) * p.grad.data**2
when I use the follow code, the loss function curve is figure 2.
figure 2
v[:] = beta1 * v + (1 - beta1) * p.grad.data
s[:] = beta2 * s + (1 - beta2) * p.grad.data**2
when I use the follow code, the loss function curve is figure 3.
figure 3
The loss function curve in case 3 has always been smoother than that in case 1.
The loss function curve in case 2 sometimes can't converge.
Why is different?
To answer the first question,
v = beta1 * v + (1 - beta1) * p.grad.data
is an out-of-place operation. Remember that python variables are references to objects. By assigning a new value to variable v, the underlying object which v referred to before this assignment will not be changed. Instead the expression beta1 * v + (1 - beta1) * p.grad.data results in a new tensor which is then referred to by v.
On the other hand
v[:] = beta1 * v + (1 - beta1) * p.grad.data
is an in-place operation. After this operation v still refers to the same underlying object, and the elements of that tensor are modified and replaced with the values of the new tensor beta1 * v + (1 - beta1) * p.grad.data.
Take a look at the following 3 lines to see why this matters
for p, (v, s) in zip(params, states):
v[:] = beta1 * v + (1 - beta1) * p.grad.data
s[:] = beta2 * s + (1 - beta2) * p.grad.data**2
v and s are actually referring to tensors which are stored in states. If we do in-place operations then the values in states are changed to reflect the value assigned to v[:] and s[:].
If out-of-place operations are used then the values in states remain unchanged.
I tried to use Scipy's fsolve to find the answers to a system of two nonlinear equations.
The two equations are:
f1 = math.log(x) + 1. - ((1. + (m - 1)*x) / m) + chi * (1 - x)**2
f2 = math.log(1 - x) - (m - 1)*x + chi*m*x**2
m and chi are constants in this case. The essential goal is to find x, y that satisfies simultaneously f1(x) = f1(y) and f2(x) = f2(y). I know the initial guess for x, y are 0.3 and 0.99. Below is my code.
from scipy.optimize import fsolve
import math
# some global variables
m = 46.663
chi = 1.1500799949128826
def binodal_fsolve():
def equations(p):
x, y = p
out = []
out.append(math.log(x) + 1. - ((1. + (m - 1)*x) / m) + chi * (1 - x)**2 - (math.log(y) + 1. - ((1. + (m - 1)*y) / m) + chi * (1 - y)**2))
out.append(math.log(1 - x) - (m - 1)*x + chi*m*x**2 - (math.log(1 - y) - (m - 1)*y + chi*m*y**2))
return out
initial_guess = [0.3, 0.99]
ans = fsolve(equations, initial_guess)
return ans
def test_answers(phiL, phiR):
def functions(x):
return math.log(x) + 1. - ((1. + (m - 1)*x) / m) + chi * (1 - x)**2, math.log(1 - x) - (m - 1)*x + chi*m*x**2
return functions(phiL)[0], functions(phiR)[0], functions(phiL)[1], functions(phiR)[1]
print (test_answers(0.2542983070, 0.9999999274))
# (1.3598772108380786e-09, -1.5558330624053502e-09, -8.434988430355375, -8.435122589529684)
res = binodal_fsolve()
print (res)
When I executed the code, I always encountered the math domain error.
However, if I tried to solve it using MAPLE fsolve. I can get the answers (0.2542983070, 0.9999999274).
By plugging these back to the equations, I get (1.3598772108380786e-09, -1.5558330624053502e-09, -8.434988430355375, -8.435122589529684) which suggests the answers are correct.
I don't know how to make scipy fsolve work. Any suggestions will be greatly appreciated.
In this case you can use the log function from numpy.lib.scimath that returns a complex number when its argument is negative.
Instead of using scipy.optimize.fsolve, use scipy.optimize.root and change the method to lm which solves the system of nonlinear equations in a least squares sense using a modification of the Levenberg-Marquardt algorithm. For more methods, see the documentation.
from scipy.optimize import root
import numpy.lib.scimath as math
# some global variables
m = 46.663
chi = 1.1500799949128826
def binodal_fsolve():
def equations(p):
x, y = p
out = []
out.append(math.log(x) + 1. - ((1. + (m - 1)*x) / m) + chi * (1 - x)**2 - (math.log(y) + 1. - ((1. + (m - 1)*y) / m) + chi * (1 - y)**2))
out.append(math.log(1 - x) - (m - 1)*x + chi*m*x**2 - (math.log(1 - y) - (m - 1)*y + chi*m*y**2))
return out
initial_guess = [0.3, 0.99]
#ans = fsolve(equations, initial_guess)
ans = root(equations, initial_guess, method='lm')
return ans
def test_answers(phiL, phiR):
def functions(x):
return math.log(x) + 1. - ((1. + (m - 1)*x) / m) + chi * (1 - x)**2, math.log(1 - x) - (m - 1)*x + chi*m*x**2
return functions(phiL)[0], functions(phiR)[0], functions(phiL)[1], functions(phiR)[1]
print (test_answers(0.2542983070, 0.9999999274))
# (1.3598772108380786e-09, -1.5558330624053502e-09, -8.434988430355375, -8.435122589529684)
res = binodal_fsolve()
print (res)
Which gives the following roots x and y: : array([0.25429812, 0.99999993]).
The full output:
(1.3598772108380786e-09, -1.5558330624053502e-09, -8.434988430355375, -8.435122589529684)
/home/user/.local/lib/python3.6/site-packages/scipy/optimize/minpack.py:401: ComplexWarning: Casting complex values to real discards the imaginary part
gtol, maxfev, epsfcn, factor, diag)
cov_x: array([[6.49303571e-01, 8.37627537e-07],
[8.37627537e-07, 1.08484856e-12]])
fjac: array([[ 1.52933340e+07, -1.00000000e+00],
[-1.97290115e+01, -1.24101235e+00]])
fun: array([-2.22945317e-07, -7.20367503e-04])
ipvt: array([2, 1], dtype=int32)
message: 'The relative error between two consecutive iterates is at most 0.000000'
nfev: 84
qtf: array([-0.00338589, 0.00022828])
status: 2
success: True
x: array([0.25429812, 0.99999993])
I've got two points between which im drawing a line (x1,y1 and x2,y2) but i need to know the coordinates of x3,y3 which is gapSize away from point x2,y2. Any ideas on how to solve this problem (the program is written in objective-c if that is helpful at all)?
You can simply calculate the angle in radians as
double rads = atan2(y2 - y1, x2 - x1);
Then you get the coordinates as follows:
double x3 = x2 + gapSize * cos(rads);
double y3 = y2 + gapSize * sin(rads);
Is this what you meant?
Compute the distance between P1 and P2: d=sqrt( (y2-y1)^2 + (x2-x1)^2)
Then x2 = (d*x1 + gapSize*x3) / (d+gapSize)
So x3 = (x2 * (d+gapSize) - d*x1) / gapSize
Similarly, y3 = (y2 * (d+gapSize) - d*y1) / gapSize
Sorry for the math. I didn't try to code it but it sounds right. I hope this helps.
There are many ways to do this. Simplest (to me) is the following. I'll write it in terms of mathematics since I can't even spell C.
Thus, we wish to find the point C = {x3,y3}, given points A = {x1,y1} and B = {x2,y2}.
The distance between the points is
d = ||B-A|| = sqrt((x2-x1)^2 + (y2-y1)^2)
A unit vector that points along the line is given by
V = (B - A)/d = {(x2 - x1)/d, (y2-y1)/d}
A new point that lies a distance of gapSize away from B, in the direction of that unit vector is
C = B + V*gapSize = {x2 + gapSize*(x2 - x1)/d, y2 + gapSize*(y2 - y1)/d}
I've got two points between which im drawing a line (x1,y1 and x2,y2) but i need to know the coordinates of x3,y3 which is gapSize away from point x2,y2. Any ideas on how to solve this problem (the program is written in objective-c if that is helpful at all)?
You can simply calculate the angle in radians as
double rads = atan2(y2 - y1, x2 - x1);
Then you get the coordinates as follows:
double x3 = x2 + gapSize * cos(rads);
double y3 = y2 + gapSize * sin(rads);
Is this what you meant?
Compute the distance between P1 and P2: d=sqrt( (y2-y1)^2 + (x2-x1)^2)
Then x2 = (d*x1 + gapSize*x3) / (d+gapSize)
So x3 = (x2 * (d+gapSize) - d*x1) / gapSize
Similarly, y3 = (y2 * (d+gapSize) - d*y1) / gapSize
Sorry for the math. I didn't try to code it but it sounds right. I hope this helps.
There are many ways to do this. Simplest (to me) is the following. I'll write it in terms of mathematics since I can't even spell C.
Thus, we wish to find the point C = {x3,y3}, given points A = {x1,y1} and B = {x2,y2}.
The distance between the points is
d = ||B-A|| = sqrt((x2-x1)^2 + (y2-y1)^2)
A unit vector that points along the line is given by
V = (B - A)/d = {(x2 - x1)/d, (y2-y1)/d}
A new point that lies a distance of gapSize away from B, in the direction of that unit vector is
C = B + V*gapSize = {x2 + gapSize*(x2 - x1)/d, y2 + gapSize*(y2 - y1)/d}