I am trying to implement a dual number type in NIM, but struggling with with, for me, obscure error messages. Most likely I am missing something trivial, but after having poured in more than 2 hours in trying stuff and researching I still could not figure it out. Here's the minimum not working example:
type
Dual*[N: static[int], T: SomeFloat | Dual] = object
real*: T
dual*: array[N, T]
proc dual*[N: static[int]; T: SomeFloat | Dual](real: T, dual: array[N, T]): Dual[N, T] =
result.real = real
result.dual = dual
# scalar multiplication
proc `*`*[N: static[int]; T: SomeFloat | Dual](x: SomeFloat; y: Dual[N, T]): Dual[N, T] =
result.real = x * y.real
for i in 0..N-1:
result.dual[i] = x * y.dual[i]
Testing this gives different errors which I put in the comments above the relevant lines of code.
# Test
#let a = dual[2, Dual[2, float]](
# dual[2, float](1.0, [0.0, 0.0]),
# [dual[2, float](0.0, [0.0, 0.0]), dual[2, float](1.0, [0.0, 0.0])]
#)
let a = dual(3.0, [1.0, 0.0])
echo a # works as expected
# Error: cannot instantiate 'Dual[N, T: float or float32 or float64 or Dual]' inside of type definition: '*'; Maybe generic arguments are missing?
echo 183.0*a
# Error: cannot instantiate: '*[2, float]'
echo `*`[2, float](183.0, a)
Thanks in advance, ~whiterock
If you want just scalar, you can change the definition of T in this case to:
# scalar multiplication
proc `*`*[N: static[int]; T: SomeFloat](x: T; y: Dual[N, T]): Dual[N, T] =
result.real = x * y.real
for i in 0..N-1:
result.dual[i] = x * y.dual[i]
# non-scalar multiplication
proc `*`*[N: static[int]; T: Dual](x: T; y: Dual[N, T]): Dual[N, T] =
# implementation here
discard
Thank you, this solves the first problem I had. I don't understand though, why it can infer N from Dual[N, T] but not T. Anyways, the problem remains that scalar multiplication should work for arbitrarily nested duals, i.e.
let a = dual[2, Dual[2, float]](
dual[2, float](1.0, [0.0, 0.0]),
[dual[2, float](0.0, [0.0, 0.0]), dual[2, float](1.0, [0.0, 0.0])]
)
but assuming the type T in Dual[N, T] can only be inferred if provided as another argument, I don't know what the function signature needs to be, so this fails:
# Error: type mismatch: got <float64, Dual[2, Dual[2, system.float]]>
echo 183.0*a
The wanted output is the following just propagating the scalar multiplication through. An analogy I could think of would be viewing matrices as a vector of vectors where one can also propagate the scalar multiplication to the vectors. To be more clear:
let want_expect = dual[2, Dual[2, float]](
dual[2, float](183.0, [0.0, 0.0]),
[dual[2, float](0.0, [0.0, 0.0]), dual[2, float](183.0, [0.0, 0.0])]
)
Thanks in advance, ~whiterock