I'm representing hyperbolic vectors using the Minowski hyperboloid: $x_0^2 - x_1^2 - x_2^2 = 1$.

I understand that the distance between two hyperbolic vectors of this form is $acosh(B(u, v))$ where $B(u, v) = u_0v_0 - u_1v_1 - u_2v_2$.

But I cannot for the life of me figure out a simple way to perform scalar and vector multiplication with the expected results using these vectors.

For scalar multiplication, for example, I have tried the following:

For one dimensional hyperbolic vectors ($1=x_0^2-x_1^2$), I have understood that scalar multiplication is equivalent to multiplying the hyperbolic angle. If $v = [cosh \theta, sinh\theta]$, then $2v$ should be $[cosh2\theta, sinh2\theta]$. Finding $\theta$ should be as trivial as taking $acos(x_0$).

Things get complicated in 2d hyperbolic space. I have toyed around with the idea of finding the angle of rotation on the "z" axis ($x_0$), and rotating that back, so that the value of $x_2$ is 0. Then applying the above function, and then rotating it back to its original state.

However the computational cost of that is immense, and it's prone to rounding error. There must be a simpler way?

I have not even begun to understand how vector addition and subtraction would work in such a space.

The wikipedia page seems to be lacking on this topic.

And please, ELI5. I have no strong math or geometry background. I am a programmer.

Note: I am storing these values using the Minowski model, rather than the Poincaré disk, as the Poincaré disk is extraordinarily prone to rounding errors on large vectors.

Edit: I have found a (still inefficient) way to do the scalar multiplication. It is nearly exactly as described above: convert the coordinates into "polar form," with a hyperbolic angle and an angle in the "z" axis. Then multiply the hyperbolic angle, then convert back. As suspected though, it does cause rounding errors.


The problem is that things like scalar multiplication and vector addition/subtraction are not well defined in hyperbolic geometry -- you could invent some definition, but then they will not have the same properties as in Euclidean, so it is probably better to avoid such operations, or call them something else. What do you need "scalar multiplication" for?

I sometimes use scalar multiplication and vector addition/subtraction which act in the whole Minkowski space (not restricted to the hyperboloid), i.e., $(x_a,y_a,z_a)+(x_b,y_b,z_b) = (x_a+x_b, y_a+y_b,z_a+z_b)$. These operations are well defined and they have some uses, for example, to find a midpoint of $(A,B)$, you add the coordinates and project the result back to the hyperboloid.

I think the rounding errors are unavoidable if you are working with hyperbolic geometry extensively. The main reason is that the hyperbolic plane grows exponentially -- a circle of radius 200 will have area of roughly $e^{200}$, so even if you are using the Minkowski hyperboloid model with three 64-bit coordinates, you have only $2^{192}$ representable points. If you have, say, two creatures starting in a small distance $\epsilon$ and going in a roughly parallel direction, the distance between them will grow to roughly $\epsilon e^d$ after each of them moves $d$ units. Likewise, the $\epsilon$ could be caused by floating point errors, so floating point errors will blow up in cases similar to this. In HyperRogue, the matrices such as the view transformation naturally accumulate floating point errors, eventually the errors build up so much that the matrix does not make any sense anymore. To fix this, the transformations are "fixed" from time to time -- this operation transforms the given matrix to a nearby isometry, losing any built up errors.

You can see our implementation of hyperbolic geometry here: GitHub (it is a bit more complex because it works with Euclidean and spherical geometry too; also, points outside of the hyperboloid are used to represent the 3D hyperbolic space in a rather weird way). Not everything is optimal in terms of computational cost, but it works good enough for HyperRogue. (The main ideas are roughly described here.) For things you would use vector addition/subtraction in Euclidean geometry, HyperRogue generally uses functions "find a matrix which rotates the given point to the x axis" or "find a matrix which translates along the x axis to move the given point to 0", which are indeed rather inefficient. But IIRC the basic things like tiling generation do not need such operations, these are more useful for complex things like movement animations. No operation similar to scalar multiplication is used -- instead there is xpush(d) which translates $d$ unit along the $X$ axis, so instead of trying to multiply a vector by a scalar, you would say what direction and distance you want, and apply the rotation and xpush(d).

  • $\begingroup$ I am attempting to define these operations because I am writing a C++ library. The inspiration for this is ZenoRogue's HyperRogue. It is a game that takes place in Hyperbolic space. Scalar multiplication could be used for things like tiling, or for movement of a vector based on delta time. Vector addition is also invaluable during game development for movement. Edit Just realized you are ZenoRogue. Hi! Thanks fo replying! $\endgroup$ – Thor Correia Jan 10 at 15:40
  • $\begingroup$ So I have been trying to comb through your code the past couple of days, and I found it a bit disorienting. I have also read and reread that dev page a couple of times. Mainly, I was having trouble finding certain classes because they are not well commented. In particular, I couldn't figure out how you translated those tiling "coordinates" into the Minowski coordinates for projection onto the Poincaré disk. I had assumed you were using vector addition: i.e. add the vector representing the distance between two tiles at a specified angle. Could you point to me what function would do this? $\endgroup$ – Thor Correia Jan 10 at 15:47
  • $\begingroup$ So to be clear, you have found this method of representing the tiles not using Minowski coordinates. You then only "translate" those coordinates into Minowski coordinates a radius of like 5 tiles away from the center, then project that onto the Poincaré disk? During the movement animation, you rotate every tile, then xpush(d) every single tile? Does xpush only work of points are at (1, 0, 0), or does it work for every point? $\endgroup$ – Thor Correia Jan 10 at 15:54
  • $\begingroup$ I have also been trying to figure out last night how you managed to draw the lines and circles in this game. I have thought of ways of finding the intersection point between a plane and the hyperboloid, and then using a small increment to generate many points on that line, and then projecting those points to poincaré disk and drawing lines between them. What function or file would this be found in? I can't seem to find it! Thanks again for your help :) $\endgroup$ – Thor Correia Jan 10 at 15:56
  • $\begingroup$ Isometries are represented by matrices. For example xpush(d) is the translation matrix ((cosh d,0,sinh d),(0,1,0),(sinh d,0,cosh d)), and spin(alpha) is the matrix which rotates by alpha. geometry.cpp computes the distance between 2 heptagons, and other relevant distances. viewctr is the "central" heptagon currently shown, and View says how to translate the coordinates relative to this heptagon into screen-relative coordinates. This View is passed to drawcell to draw that heptagon. Other heptagons nearby will have their to-screen translation matrices computed by multiplying View and push/spin. $\endgroup$ – Zeno Rogue Jan 10 at 21:18

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.