Constrain direction with linear programming (simplex) - java

I want to make a modular 2D spaceship game. The user can use building blocks to create the ship. Blocks can be squares but also other shapes that do not fit in a grid. To calculate what thrusters should be firing with user input I use ojAlgo to solve a linear programming problem. The user input will be something like: [x: 1, y: 0, z 0] where a 1 is the maximum thrust that can be given in the direction and -1 the same to the other side. Z is rotation here.
I have two problems:
The first has to do with the way I calculate x and y separate:
final ExpressionsBasedModel tmpModel = new ExpressionsBasedModel();
final Expression expressionX = tmpModel.addExpression("x").weight(1); //This is the user input.
final Expression expressionY = tmpModel.addExpression("y").weight(0).level(0); //Level means lower == 0 and upper == 0
final Expression expressionZ = tmpModel.addExpression("z").weight(0).level(0);
for (int i = 0; i < size; i++) {
ThrusterBlock thrusterBlock = blockStash.thrusterList.get(i);
final Variable thrusterVar = Variable.make("th_" + i);
thrusterVar.lower(0).upper(1);
tmpModel.addVariable(thrusterVar);
expressionX.setLinearFactor(thrusterVar, thrusterBlock.displacement.x);
expressionY.setLinearFactor(thrusterVar, thrusterBlock.displacement.y);
expressionZ.setLinearFactor(thrusterVar, thrusterBlock.displacement.z);
//The thruster displacement is the calculated thrust in the given direction
}
Optimisation.Result tmpResult = tmpModel.maximise(); //To get the maximum weight (& thrust) out.
This works because i can set the weight to > 0 or < 0 and it will return for each thruster a value between 0 and 1. With a layout with 9 thrusters this could be a output:
OPTIMAL 1.203 # [1.0, 1.0, 0.0, 0.0, 0.32, 0.32, 1.0, 1.0, 1.0]
[
0th [x: 0.401, y: 0.0 , z: -0.022]
1th [x: 0.401, y: 0.0 , z: -0.022]
2th [x: -0.401, y: 0.0 , z: -0.027]
3th [x: -0.401, y: 0.0 , z: -0.027]
4th [x: 0.0, y: 0.401 , z: 0.025]
5th [x: 0.0, y: -0.401 , z: 0.025]
6th [x: 0.0, y: -0.401 , z: 0.025]
7th [x: 0.0, y: 0.401 , z: 0.025]
8th [x: 0.401, y: 0.0 , z: -0.022]
]
The problem is that i can't use this system to move diagonally (e.g. 40°) because
final Expression expressionX = tmpModel.addExpression("x").weight(1);
final Expression expressionY = tmpModel.addExpression("y").weight(1);
Won't constrain the equation to a 1:1 x & y ratio but find the most rewarding solution. I can't use level here because i don't know the max thrust for both.
What would be the best way to constrain x & y so that i can use degrees?
The second question:
When i want the ship to turn, it does so about 45° and then turns back. While the control system shows that the same thrusters are firing. I am sure there is a problem with the way i calculate the force but my experience with angle calculations is low. I think the mistake can be found here:
public Vector2 getForce(float power, boolean relative){
float force = maxThrust * power;
float angle = toRad(this.relativePosition.z) + (relative ? 0 : this.blockCluster.getPos().z);
//relativePosition.z is the orientation of the block in degree and this.blockCluster.getPos().z is the ship rotation in radians
temp.x = force * ((float) Math.cos(angle)) * -1;
temp.y = force * ((float) Math.sin(angle));
return temp;
}
I think it has something to do with Cosines and Sinus returning only 90° but i am not sure.
Lastly, any optimisation would be greatly appreciated since this might run for multiple frames in a row. I will save the results of the calculation if it is on the slow side.
Thanks for your time!
ps: Ported from 'Game Development' because the implementation is more general then the use case.

Related

Calculate position with distance and coordinates in Java

I have a device, let's call it the origin, which sends a signal to other machines, let's call them satellites. For each satellite I know its position and the distance from the origin.
Satellite position 1: [-500, -200].
Satellite position 2: [100, -100].
Satellite position 3: [500, 100].
Satellite distance 1: 100.0
Satellite distance 2: 115.5
Satellite distance 3: 142.7
Supposedly the position of the origin is:
x: -100.0
y: 75.5
I say supposedly because I don't know if it really is, I don't know how to calculate this.
I had seen this library: https://github.com/lemmingapex/Trilateration. But the result it gives me is a bit different from what I expected.
With the library, i try this:
double[][] positions = new double[][] { { -500.0, -200.0 }, { 100.0, -100.0 }, { 500.0, 100 } };
double[] distance = new double[] { 100.0, 115.5, 142.7 };
NonLinearLeastSquaresSolver solver = new NonLinearLeastSquaresSolver(new TrilaterationFunction(positions, distances), new LevenbergMarquardtOptimizer());
Optimum optimum = solver.solve();
double[] centroid = optimum.getPoint().toArray();
And the result is [-58.315252587138595, -69.55141837312165], not [-100, 75.5].
The thing is, I don't know if I'm using the right function or if I got the answer wrong.

How can we write the angle expression for analog clock?

This is the expression given as solution in the book I referred to but it seems to be beyond my understanding. Please anyone help to understand how are we exactly writing this angle? Why subtract pi/2? I really have no idea
xs=(int)(Math.cos(s*3.14f/30-3.14f/2)*45+xcentre);
ys=(int)(Math.sin(s*3.14f/30-3.14f/2)*45+ycentre);
xm=(int)(Math.cos(m*3.14f/30-3.14f/2)*40+xcentre);
ym=(int)(Math.sin(m*3.14f/30-3.14f/2)*40+ycentre);
xh=(int)(Math.cos((h*30+m/2)*3.14f/180-3.14f/2)*30+xcentre);
yh=(int)(Math.sin((h*30+m/2)*3.14f/180-3.14f/2)*30+ycentre);
Seems like it's giving the cartesian coordinates of the ends of the three hands of a clock centered at the origin, with up being negative y, and right being positive x. I can say this just by trying some values.
I wrapped your basic code like this:
static class ClockCoords {
int xs, ys;
int xm, ym;
int xh, yh;
public String toString() {
return String.format("h: %3d,%3d m: %3d,%3d s: %3d,%3d", xh, yh, xm, ym, xs, ys);
}
}
public static ClockCoords coords(int h, int m, int s) {
ClockCoords r = new ClockCoords();
int xcentre = 0;
int ycentre = 0;
r.xs = (int) (Math.cos(s * 3.14f / 30 - 3.14f / 2) * 45 + xcentre);
r.ys = (int) (Math.sin(s * 3.14f / 30 - 3.14f / 2) * 45 + ycentre);
r.xm = (int) (Math.cos(m * 3.14f / 30 - 3.14f / 2) * 40 + xcentre);
r.ym = (int) (Math.sin(m * 3.14f / 30 - 3.14f / 2) * 40 + ycentre);
r.xh = (int) (Math.cos((h * 30 + m / 2) * 3.14f / 180 - 3.14f / 2) * 30 + xcentre);
r.yh = (int) (Math.sin((h * 30 + m / 2) * 3.14f / 180 - 3.14f / 2) * 30 + ycentre);
return r;
}
public static void main(String[] args) throws IOException {
System.out.println(coords(0, 0, 0));
System.out.println(coords(0, 0, 45));
System.out.println(coords(6, 0, 0));
System.out.println(coords(6, 30, 0));
}
which gives me this:
h: 0,-29 m: 0,-39 s: 0,-44
h: 0,-29 m: 0,-39 s: -44, 0
h: 0, 29 m: 0,-39 s: 0,-44
h: -7, 28 m: 0, 39 s: 0,-44
So, if I haven't messed this up, the hour hand is 29 units long, the minute hand is 39 units long, and the second hand is 44 units long. At the start of the day, the first entry, they're all pointing straight up. 45 seconds into the day, the second entry, sees the second hand pointing off to the left, but the other two hands are still point straight up given integer granularity and hands that aren't too long. The third entry, 6:00, just flips the hour hand to point down. The fourth entry is interesting, 6:30, in that it points the minute hand down, but also moves the hour hand a little to the left and up...our first hand in a position not at a 90 degree angle to center. Interesting stuff. As to the WHY of all of this...I assume it's all basic trig and cartesian math.
Wait a sec...there one thing strange I don't get...I see in the formulas where each hand is given a different length. Why are the hand lengths I see off by 1 from what's in the formulas? My guess is that it has to do with the very rough approximation of PI that's used. I think those hands aren't pointing quite straight up. That's all I can think of. Maybe tomorrow I'll try plugging in a better value for PI.
Let me provide you some background information as well.
Trigonometry
is all about a circle, having a radius of 1 unit, centered in the origo, that is, point (0, 0). The coordinates of a point on the circle can be calculated as x = cos(alpha), y = sin(alpha), where alpha is the angle. Since all the points of the circle are of equal distance from the center, you only need to know the angle to compute the coordinates, which are (cos(alpha), sin(alpha)).
Reason: Look at Pythagoras's theorem, which is, by the way, a consequence of the law of cosines, where the largest angle of the triangle is of 90 degree.
Pythagoras's theorem states that
where a, b and c are lengths of sides of the triangle, the triangle being perpendicular and a < c > b is true. So, the cos and sin is due to trigonometrical coordinates.
Radians, PI/2
Units of angle can be measured in degrees (360 degrees being the full circle) or radians (2 PI being the full circle). Let's talk in radians. In math, 0 radians is the angle of the upmost point of the circle. pi/2 is the left-most. pi is the downmost. 3pi/2 is the rightmost. Notice, that as the angle increases your point moves counter-clockwise.
Since this is periodic, it is true that
2 * k * pi + alpha = alpha
On screens, y points downwards, by contrast to the standard in math, where y vertex points upwards. So, if you start from 0 radians, that will be the downmost point when shown on the screen and due to the invert y, as you increase the angle, you move to the clockwise direction. Removing pi / 2 from 0 points you to the rightmost point.
Variables
(xh, yh) are the hour coordinates
(xm, ym) are the minute coordinates
(xs, ys) are the second coordinates
They are the hands of a traditional clock.
h is the hour angle
m is the minute angle
s is the second angle
(xcentre, ycentre) are the coordinates of the center. They might be different from (0, 0), so coordinate moving, the so-called translation is supported.
Step sizes
There are 60 seconds in a minute and there are 60 minutes in an hour, so the angle step needs to be the same for them, even though, the frequency of steps is different, while there are 24 hours in a day, so the angle step of hour needs to be both less frequent and larger (12 positions on a traditional clock).
Length of hands
Hour, minute and second hands (no pun intended) differ in their length, this is why the unit of the trigonometric circle is multiplied by 45, 40 and 30, respectively.
As time passes, presumably the angles are changed and subsequently their coordinates change.
A less-exhaustive explanation, looking only at seconds:
xs = (int)(Math.cos(s*3.14f/30-3.14f/2)*45+xcentre);
ys = (int)(Math.sin(s*3.14f/30-3.14f/2)*45+ycentre);
As Steve's answer notes, drawing a line from (xcentre, ycentre) to (xs, ys) would display a seconds-hand, with "up" being "y = 0" -- so that axis is reversed compare to traditional plots. This actually works in our favor: normally, as angles grow, they are displayed as going anti-clockwise. Since the y axis is reversed in screen coordinates, this is fine for us: our hands will advance clockwise, as expected.
Let us look at the maths, using xs:
xs = Math.cos(s*3.14f/30-3.14f/2)*45; // removed rounding & using xcentre=0
xs = Math.cos((s/30)*PI - PI/2) * sHandLength; // use PI=3.14..., sHandLength=45
Now, at time 0, we want the seconds-hand to look straight up. But normally cos(0) = 1. If we substract 90º (PI/2), it will be looking straight up:
xs = Math.cos((s/30)*PI - zeroIsUp) * sHandLength;
We can undo this to keep on reasoning about the remaining expression:
xs = Math.cos((s/30)*PI); // sHandlength of 1, time 0 is left
All that is missing is that mysterious s/30*PI. Let's make a small table:
seconds(s) radians(argument to Math.cos())
0 0
15 PI/2 -- a quarter-turn of the hand
30 PI -- a half-turn
45 3*PI/2 -- a three-quarters turn
60 2*PI, or 0 -- a full turn
So it seems that it is simply that (s/30) * PI is short for (s/60) * (2*PI).
For a book on Java, I would have expected a much clearer way of writing this. My personal preference would have been:
xs = (int)(Math.cos(base60toRadians(s) - zeroIsUp) * secondHandLength + xCentre);
ys = (int)(Math.sin(base60toRadians(s) - zeroIsUp) * secondHandLength + yCentre);
// ... somewhere else
private float base60toRadians(int b60) { return (b60 / 60) * (2 * Math.PI); }
The coordinates of the clock's centre are (xcentre, ycentre).
With a geometric affine translation to the centre you can calculate like from (0, 0).
The hands are rotating clock wise, in the negative direction.
Now the hands of a clock start at the top, and (0, 1), which has an angle π/2 (90°). (1, 0) being where the angle starts, (-1, 0) halfways having angle π (180°).
The full 360° angle being 2π, 1/60th thereof becomes:
Seconds: 60 seconds = 2π; angle 2π / 60 = π / 30
double startAngle = Math.PI / 2; // Value 0 has angle π/2
double rs = 45;
xs = (int)(Math.cos(-s * Math.PI / 30 - startAngle) * rs + xcentre);
ys = ... sin ...
Hours: 24 hours = 2π; angle 2π / 24 = π / 12

Cubic equation graph in JavaFX.

I have really not found any good sources where this solution/idea is presented. We started with JavaFX in my class and I have that homework.
I have a equation that should build a graph in JavaFX. I have the canvas ready.
For example y = 4x^3 + 3x^2 - 3x + 1.
Here we can calculate some points:
x = -1, y = -4 + 3 + 3 + 1 = 3
x = 0, y = 1
x = 1, y = 5
x = 2, y = 4 * 2^3 + 3 * 2^2 - 3 * 2 + 1 = 39
As I can imagine, the idea is to stake step of about 0.1.
But still I have no idea how to code that thing. Professor said, that our code has to solve any cubic equation. Bonus points if graph is centered around extremum points.
If you have to find the extrema anyway, look for the inflection point (root of the second derivative; this is elementary). You can center the plot on this point, as it lies in the middle of the extrema. By finding the roots of the first derivative, you will locate these extrema, if they exist.
By checking the signs of the function at the extrema, you will know how many roots the function has (1 or 3), and where they can be located.
This is enough to find their precise location using the method known as "regula falsi".

Picking from list randomly according to Pareto Principle

I have a List<T> and try to randomly pick items according to Pareto Principle, so first 20% items will be picked 80% times, and 80% remaining items will be picked 20% times. So far I have a simple implementation:
static <T> T pickPareto(List<T> list) {
int n = list.size();
int first = n * 0.2;
return rnd.nextFloat() < 0.8
? list.get(rnd.nextInt(first)) // pick one of first 20%
: list.get(first + rnd.nextInt(n - first)); // pick one of remaining 80%
}
It works well, but picks items according to a distribution which is a step function.
Does anyone know how to select items according to distribution which is smooth function (maybe not exactly Pareto, but holding 20/80 property)?
After spending some time on research, I discovered that this problem can be reduced to a problem of finding the function, which applied to a function producing uniform random distribution (.nextFloat() for example), results desired distribution.
Such function f(x) must meet all following conditions:
f(0) = 0
f(x) → 1 for x → 1
be non-decreasing, better strictly increasing, on interval [0, 1)
be smooth on interval [0, 1)
f(0.8) = 0.2 -- condition of 80/20 Pareto Principle, or, in common, f(p) = 1 - p
Finally, I succeeded with such function. It can be:
f(x) = (xa + 1 – (1 – x)1/a) / 2,a = logp(1 – p)
Here argument p ∈ (0, 1) means exactly what it means in condition 5: it is an adjustment parameter showing how resulting distribution will differ from uniform. For example, if p = 0.8 then f(0.8) = 0.2. If p = 0.5, then a = 1 so function collapses to f(x) = x.
Chart for p = 0.8:
So the method to pick from list will look like:
public static <T> T pickRandomly(List<T> list, float p) {
if (p <= 0 || p >= 1.0)
throw new IllegalArgumentException();
double a = Math.log(1.0 - p) / Math.log(p);
double x = rnd.nextDouble();
double y = (Math.pow(x, a) + 1.0 - Math.pow(1.0 - x, 1.0 / a)) / 2.0;
return list.get((int) (list.size() * y));
}
For example, picked 1000 times from list of 10 integers, p = 0.8:
0: 646
1: 153 // 0 or 1 occured 799 times
2: 60
3: 57
4: 32
5: 26
6: 18
7: 7
8: 1
9: 0
Use nextFloat(), it will give you the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 from this random number generator's sequence.
return rnd.nextFloat() < 0.8
? list.get(rnd.nextInt(first)) // pick one of first 20%
: list.get(first + rnd.nextInt(n - first)); // pick one of remaining 80%
Also, I presume rnd is float.

Using precomputed kernel in libsvm causes it to get stuck

We are two students who want to use one-class svm for dectection of summary worthy sentences in text documents. We have already implemented sentence similarity functions for sentences, which we have used for another algorithm. We would now want to use the same functions as kernels for a one-class svm in libsvm for java.
We are using the PRECOMPUTED enum for the kernel_type field in our svm_parameter (param). In the x field of our svm_problem (prob) we have the kernel matrix on the form:
0:i 1:K(xi,x1) ... L:K(xi,xL)
where K(x,y) is the kernel value for the similarity of x and y, L is the number of sentences to compare and i is the current row index (0 to L).
The training of the kernel (svm.svm_train(prob, param)) seems to get sometimes get "stuck" in what seems like a infinite loop.
Have we missunderstood how to use the PRECOMPUTED enum, or does the problem lay elsewhere?
We solved this problem
It turns out that the "series numbers" in the first column needs to go from 1 to L, not 0 to L-1, which was our initial numbering. We found this out by inspecting the source in svm.java:
double kernel_function(int i, int j)
{
switch(kernel_type)
{
/* ... snip ...*/
case svm_parameter.PRECOMPUTED:
return x[i][(int)(x[j][0].value)].value;
/* ... snip ...*/
}
}
The reason for starting the numbering at 1 instead of 0, is that the first column of a row is used as column index when returning the value K(i,j).
Example
Consider this Java matrix:
double[][] K = new double[][] {
double[] { 1, 1.0, 0.1, 0.0, 0.2 },
double[] { 2, 0.5, 1.0, 0.1, 0.4 },
double[] { 3, 0.2, 0.3, 1.0, 0.7 },
double[] { 4, 0.6, 0.5, 0.5, 1.0 }
};
Now, libsvm needs the kernel value K(i,j) for say i=1 and j=3. The expression x[i][(int)(x[j][0].value)].value will break down to:
x[i] -> x[1] -> second row in K -> [2, 0.5, 1.0, 0.1, 0.4]
x[j][0] -> x[3][0] -> fourth row, first column -> 4
x[i][(int)(x[j][0].value)].value -> x[1][4] -> 0.4
This was a bit messy to realize at first, but changing the indexing solved our problem. Hopefully this might help someone else with similar problems.

Categories