I'm new to Prolog and my task requires finding all equilateral triangles which are built from DB points. As a result, I'm getting identical points of the triangles. I do not understand what the problem is. Help me pls!
:- dynamic
point/3.
%?Point_Sign, ?Abscissa, ?Ordinate
db_filling:-
point(_,_,_),!.
db_filling:-
assert(point(a,1,1)),
assert(point(b,1,2)),
assert(point(c,1,3)),
assert(point(d,2,2)),
assert(point(e,3,3)),
assert(point(f,-1,1)),
assert(point(g,-2,1)),
assert(point(h,-2,2)),
assert(point(i,-3,3)),
assert(point(j,-3,-1)),
assert(point(k,-3,-2)),
assert(point(l,-3,-3)),
assert(point(n,-1,-1)),
assert(point(m,-3,0)),
assert(point(o,3,0)),
assert(point(p,0,3)).
% +List
points_main(Xs):-
db_filling,
findall(Xs1,equilateral_triangles(Xs1),Xs).
% +Points
equilateral_triangles([P1,P2,P3]):-
point(P1,X1,Y1),
point(P2,X2,Y2),
point(P3,X3,Y3),
L1 is sqrt((X2 - X1)^2 + (Y2 - Y1)^2),
L2 is sqrt((X3 - X2)^2 + (Y3 - Y2)^2),
L3 is sqrt((X1 - X3)^2 + (Y1 - Y3)^2),
L1 = L2,
L2 = L3.
Result:
?- points_main(Res).
Res = [[a, a, a], [b, b, b], [c, c, c], [d, d, d], [e, e, e], [f, f, f], [g, g|...], [h|...], [...|...]|...].
You have a couple of fundamental issues here.
From memory, the two-dimensional coordinates of an equilateral triangle cannot all be integers. You must have at least one irrational number. Computers are not good at representing irrational numbers.
So, you're also confusing real maths with computer maths. You can't simply get the sqrt and check for equality.
To illustrate I produced these three points:
point(j1,0,0).
point(j2,1,0).
point(j3,0.5,0.866025403784439). % 1/2 & sqrt(3)/2
That's an equilateral triangle.
When I run that with your code, the lengths that get produced are like this:
[1.0,1.0000000000000004,1.0000000000000004]
They are all effectively 1.0, but of course 1.0000000000000004 is not 1.0. So, even this doesn't comeback as a equal.
So you are really forced to check the confidence as an epsilon to say two numbers are equal.
Here's what I did to do that:
points_main(Xs):-
findall(Xs1,equilateral_triangles(Xs1),Xs).
equilateral_triangles([P1,P2,P3]):-
point(P1,X1,Y1),
point(P2,X2,Y2),
P1 \= P2,
point(P3,X3,Y3),
P1 \= P3,
P2 \= P3,
L12 is sqrt((X2 - X1)^2 + (Y2 - Y1)^2),
L23 is sqrt((X3 - X2)^2 + (Y3 - Y2)^2),
L31 is sqrt((X1 - X3)^2 + (Y1 - Y3)^2),
D1223 is abs(L12 - L23),
D1223<0.00000001,
D2331 is abs(L23 - L31),
D2331<0.00000001,
D3112 is abs(L31 - L12),
D3112<0.00000001.
Now, if I run that against my points above I get this:
?- points_main(Xs).
Xs = [[j1, j2, j3], [j1, j3, j2], [j2, j1, j3], [j2, j3, j1], [j3, j1, j2], [j3, j2, j1]].
All combinations of the above three points - so, yes, they are all equilateral triangles.
If I run against your original points, as expected, there are no equilateral triangles.
A few side notes.
(1) I removed all of the assert code. It wasn't helpful nor necessary to get your code working.
(2) you could define my j3 point as point(j3,0.5,X) :- X is sqrt(3)/2. and your original maths would work. However, this is just lucky. When dealing will floating-point numbers you can never be sure that two numbers are equal even though they should be.
(3) I introduced P1 \= P2, etc, to prevent the points unifying to themselves. That's why you got [a,a,a],... etc.
The reason you get odd results is that your
...
point(P1,X1,Y1),
point(P2,X2,Y2),
point(P3,X3,Y3),
...
is producing the Cartesian Product of your set of points. If you had just 4 points defined, this would produced 43 (64) possible results.
Since you are talking triangles here, where for a given 3 points A, B, and C, whether the triangle they describe is labelled ABC, ACB, BAC, BCA, CAB, CBA is irrelevant: they all describe exactly the same triangle, so what your are looking for are combinations: a selection of items from a set where order is not important, so {a,b,c] and {c,b,a] are the same combination.
In Prolog, getting combinations is easy:
combination( [] , [] ) .
combination( [H|T] , [H|T2] ) :- combination(T,T2).
combination( [_|T] , T2 ) :- combination(T,T2).
The only caveat is that the 2nd argument to combination/2 must be seeded as a list of the desired length. To draw combinations of 3 things from a set of 5 things is as easy as:
combination( [a,b,c,d,e] , [X,Y,Z] ).
Once you have that, getting the set of points from which to draw is easy, too, using findall/3 or setof/3:
setof( p(P,X:Y) , point(P,X,Y) , Ps ) ,
The one tricky thing is that floating point arithmetic leaves much to be desired, what with floating point jitter and all. This is how I'm computing distance:
%-----------------------------------------------------------------------
% computes the distance between two points on the Cartesian plane.
% the distance, D, is returned as an integer with an implied scale of 5,
% so the distance 1.23456789 is returned as 123457 (rounded up)
% ----------------------------------------------------------------------
distance( X1:Y1 , X2:Y2 , D ) :-
V is sqrt( (X2-X1)^2 + (Y2-Y1)^2 ) ,
D is round( 100000 * V )
.
Putting it together, we get this (https://swish.swi-prolog.org/p/zHAfMTtA.pl):
equilateral_triangle( T ) :-
setof( p(P,X:Y) , point(P,X,Y) , Ps ) ,
combination(Ps,[A,B,C]),
equilateral_triangle(A,B,C,T)
.
%--------------------------------------------------------------------------------
% combination( +Set, +Combination )
%
% Takes a set (a list of distinct things) of M things and produces
% on backtracking all the combinations of M things taken N at a time.
%
% Combination, the 2nd argument MUST be initialize as a list of the desired
% length. For example, to get all the combination of 8 things taken 3 at a time,
% you'd say something like this:
%
% ?- length(C,3), combination([a,b,c,d,e],C).
%
% And get back
%
% C = [a, b, c]
% C = [a, b, d]
% C = [a, b, e]
% C = [a, c, d]
% C = [a, c, e]
% C = [a, d, e]
% C = [b, c, d]
% C = [b, c, e]
% C = [b, d, e]
% C = [c, d, e]
%
%--------------------------------------------------------------------------------
combination( [] , [] ) .
combination( [H|T] , [H|T2] ) :- combination(T,T2).
combination( [_|T] , T2 ) :- combination(T,T2).
%--------------------------------------------------------------
% 3 points comprise an equilateral triangle if the triangle
% that they describe has legs of equal length
%--------------------------------------------------------------
equilateral_triangle( p(A,Pa), p(B,Pb), p(C,Pc) , t(A,B,C) ) :-
distance(Pa,Pb,D),
distance(Pb,Pc,D),
distance(Pc,Pa,D).
%-----------------------------------------------------------------------
% computes the distance between two points on the Cartesian plane.
% the distance, D, is returned as an integer with an implied scale of 5,
% so the distance 1.23456789 is returned as 123457 (rounded up)
% ----------------------------------------------------------------------
distance( X1:Y1 , X2:Y2 , D ) :-
V is sqrt( (X2-X1)^2 + (Y2-Y1)^2 ) ,
D is round( 100000 * V )
.
point( a , 0 , 0 ) .
point( b , 5 , 0 ) .
point( c , 2.5 , 4.33012701892 ) .
point( d , 1 , 1 ) .
point( e , 6 , 1 ) .
point( f , 3.5 , 5.33012701892 ) .
As part of a raytracer experiment I'm working on in my high school classes, I need to make it so that I can get the 4 parts of a plane equation from 3 different points. By 4 parts i mean in the equation Ax + By + Cz = D I need to find A, B, C, and D. I understand the math behind this as its relatively simple vector math, but my code doesn't seem to work.
The function I use to construct the Plane object from the 3 points is as follows:
Plane::Plane(Vec3 A, Vec3 B, Vec3 C)
{
//Getting both vectors
Vec3 AB = B - A;
Vec3 AC = C - A;
//Cross Product
Vec3 cr = AB.cross(AC);
a = cr.getX();
b = cr.getY();
c = cr.getZ();
d = a * A.getX() + b * B.getY() + c * C.getZ();
}
In this, Vec3 is just a vector class that holds (x, y, z), and the function names are pretty self explanatory (I hope).
An example of what it outputs:
If I put the vectors (-3, 0, 1), (2, 3, 0), and (0, 2, 3) into this, I get the following results
A = 8
B = -13
C = 1
D = -60
A, B, and C in this are correct, but D is not.
I'm not entirely certain what's wrong with the code, since it will sometimes get the output correctly on certain vectors, sometimes get parts correct, or sometimes get nothing correct at all, which leads me to believe there's a math mistake. Any help is appreciated.
Since in your example, you get the values for A, B, and C correct, the first place to look is in the calculation of D.
In your calculation of d, you use parts of three different vectors. This is not what the equation for D says to do. You want to use the three parts from one vector.
d = a * A.getX() + b * A.getY() + c * A.getZ();
This should work for any of the three vectors.
I have two numpy arrays: A of shape (b, i) and B of shape (b, o). I would like to compute an array R of shape (b, i, o) where every line l of R contains the outer product of the row l of A and the row l of B. So far what i have is:
import numpy as np
A = np.ones((10, 2))
B = np.ones((10, 6))
R = np.asarray([np.outer(a, b) for a, b in zip(A, B)])
assert R.shape == (10, 2, 6)
I think this method is too slow, because of the zip and the final transformation into a numpy array.
Is there a more efficient way to do it ?
That is possible with numpy.matmul, which can do multiplication of "matrix stacks". In this case we want to multiply a stack of column vectors with a stack of row vectors. First bring matrix A to shape (b, i, 1) and B to shape (b, 1, o). Then use matmul to perform b times the outer product:
import numpy as np
i, b, o = 3, 4, 5
A = np.ones((b, i))
B = np.ones((b, o))
print(np.matmul(A[:, :, np.newaxis], B[:, np.newaxis, :]).shape) # (4, 3, 5)
An alternative could be to use numpy.einsum, which can directly represent your index notation:
np.einsum('bi,bo->bio', A, B)
Why not simply
A[:, :, None] * B[:, None, :]
Depending on your convention and your dtype, you might need to throw in another np.conj somewhere. Note that np.newaxis is simply None