Classifying a set of stars by their relative positions​
​by Rishi Soni

Introduction

Identifying and visualizing stars in space can be a difficult task due to the variability and number of stars in space. Therefore, I wanted to apply computational thinking to help solve this issue. The purpose of my project is to classify the stars belonging a specific area of the sky using only their relative positions. My program accomplishes this task using an algorithm that compares the distances between these stars to the distances between the 300 brightest stars in the sky. The algorithm needs to be inputted the relative positions of the stars in an area of the sky, and the algorithm will output the predicted identities of these stars and an illustration of where these stars are in the overall sky. I have created this project through the 2020 Wolfram High School Summer Camp.

Acquiring and Formatting Data

To accomplish my algorithm, I needed data on the brightest stars in the sky. Luckily, the Wolfram language has all of the data that I need already built-in.
Collecting data from Wolfram:
brighestStars=EntityListEntityClass"Star",
apparent magnitude
TakeSmallest[300];​​brighestStarsLocations=FromDMS/@#&/@EntityValue[brighestStars,{"Altitude","Azimuth"}];
Apparent magnitude indicates the brightness of a star, except it works in an inverse manner. Therefore, by taking the stars with the 300 stars with the lowest apparent magnitude, I was able to collect the 300 brightest stars. I take the altitude and azimuth for each of the stars, which represents the angular position of a star in relation to the Earth. After that, I convert the values from DMS into decimal form. Here is an illustration of how the position of a star works:​
Formatting the data:
brighestStarsLocations={#[[1]],#[[2]]-180}&/@brighestStarsLocations;
The azimuth was in the range of 0 - 360 degrees, and I needed it the range of -180 - 180 degrees. Therefore, I translated the values down by 180.

Implementing the Classification Algorithm

Creating a Star Area Selector

Before I could directly work on the classifier, I needed a method of selecting the positions of stars that are in a certain area of the sky. It is necessary to do this so that I have some way of testing my algorithm. I accomplished this through a function called "findInRegion" that would collect the positions of stars in selected ranges of altitude and azimuth. After that, I created a function called "visualizeStars" that visualizes the positions of the stars in the sky. I used these two functions along with a helper function to create a dynamic, graphical way of selecting an area of stars. The selector is sadly very glitchy in the Wolfram Cloud, so I have just imported the selector as an image.
Implementing the star area selector:
In[]:=
findInRegion[theta_,phi_,thetaSpan_,phiSpan_]:=Select[brighestStarsLocations,theta≤#[[1]]≤theta+thetaSpan&&phi≤#[[2]]≤phi+phiSpan&];​​convertToDisplayable[points_]:=FromSphericalCoordinates[Join[{1},Degree*#]]&/@({#[[1]]+90,#[[2]]}&/@points);​​visualizeStars[points_]:=​​ListPointPlot3D[{Style[convertToDisplayable[points],Orange],Table[{n,0,0},{n,-1,1,0.01}]},​​BoxedFalse,​​BoxRatios{1,1,1},​​PlotRange{{-1,1},{-1,1},{-1,1}},​​AxesFalse,​​ImageSizeFull​​];​​Quiet[Manipulate[Column[{visualizeStars[findInRegion[θ,ϕ,RangeOfθ,RangeOfϕ]],{θ,ϕ,RangeOfθ,RangeOfϕ}}],​​{θ,-90,90},​​{ϕ,-180,180},​​{{RangeOfθ,90},0,180},​​{{RangeOfϕ,180},0,360},​​ContentSize->{600,600},​​SaveDefinitionsTrue​​]]
Out[]=

Finding the Distances between the Brightest Stars in the Sky

The second step to implementing the classifier was simply creating a distance function. My algorithm relies on finding the distances between neighboring points, so a distance function will be crucial to the success of the this program. The formula that I implemented is known as Haversine’s Formula, and it allows me to calculate the great-circle distance between two spherical points. Using the distance function, I created a distance matrix between all of the 300 brightest stars. In addition, I created a sorted version of this matrix that allows me to find the distances between a point and its nearest neighbors. By creating these matrices beforehand, I will be able to cut down runtime while executing the classification function.
Creating distance function and distance matrices:
distance[{theta1_,phi1_},{theta2_,phi2_}]:=​​2*ArcSin@Sqrt[Haversine[(theta2-theta1)Degree]+Cos[theta1*Degree]Cos[theta2*Degree]Haversine[(phi2-phi1)Degree]]​​distanceMat=Table[distance[brighestStarsLocations[[i]],brighestStarsLocations[[j]]],​​{i,Length[brighestStarsLocations]},​​{j,Length[brighestStarsLocations]}​​];​​sortedDistanceMat=Sort/@distanceMat;

Classifier Implementation

Now that I have all prerequisites for my classifier, I can actually implement the algorithm. The algorithm is composed of many steps. It first delete the outliers from the inputted relative positions. After that, it finds the position that is the most central among all inputted positions. The reason that outliers were previously deleted is so that this central point isn't skewed in a direction. Next, it finds the sorted distances between the most central point and all other inputted points. It then iterates through each position on the sorted distance matrix and examines the distances for each. The algorithm finds the set of distances that is nearest to the distances of the inputted points. It is important to know that all of the distances are rescaled to account for different scales. At this point in the classifier, I have the row of distances that is most closely associated with the distances of my inputted positions. The identities of the stars that these distances correspond to is revealed by finding the positions of these distances in the initial distance matrix.
The classification algorithm:
classifyStars[points_]:=Module[{formattedPoints,centralPoint,patchDistances,starsChecked,knownRow,starIdentities},​​formattedPoints=DeleteAnomalies[points];​​centralPoint=Flatten[Nearest[formattedPoints,​​{​​(Max[formattedPoints[[All,1]]]+Min[formattedPoints[[All,1]]])/2,​​(Max[formattedPoints[[All,2]]]+Min[formattedPoints[[All,2]]])/2​​}​​]];​​patchDistances=Sort[distance[#,centralPoint]&/@formattedPoints];​​starsChecked=Floor[Length[formattedPoints]/2];​​knownRow=Flatten[Position[N/@Rescale/@sortedDistanceMat[[All,1;;starsChecked]],​​Flatten[Nearest[N/@Rescale/@sortedDistanceMat[[All,1;;starsChecked]],​​N/@Rescale[patchDistances[[1;;starsChecked]]]]]]][[1]];​​starIdentities=Flatten[Table[Position[distanceMat[[knownRow]],n],{n,sortedDistanceMat[[knownRow,1;;starsChecked]]}]]​​]
Now that I have the identities of the stars, I can make a nice output showing the star names and their overall position in the sky.
Generate output using star identities:
generateOutput[identities_]:=​​Panel[​​ListPointPlot3D[​​{​​Style[convertToDisplayable[brighestStarsLocations[[identities]]],Red],​​Style[convertToDisplayable[brighestStarsLocations[[Complement[Range[Length[brighestStarsLocations]],identities]]]],Blue],​​Table[{n,0,0},{n,-1,1,0.01}]​​},​​BoxRatios{1,1,1},​​PlotRange{{-1,1},{-1,1},{-1,1}},​​BoxedFalse,​​AxesFalse,​​PlotLegendsPointLegend[{Red,Blue},{"Predicted Stars in Area","Remaining Stars"}],​​ImageSizeLarge​​],​​Style[InsertLinebreaks[ToString@StringForm["Predicted Stars: ``",#["Name"]&/@brighestStars[[identities]]],140],Bold],​​{{Bottom,Center}}​​]

Classifier Output

A Notable Attempt

As of right now, the only way to use the program is to input a set of relative positions in the form of spherical coordinates. I realized that in order to make the program more practical, there needs to be capabilities for more types of input. Therefore, I attempted to make it possible to input an image of a star configuration. This is the image of the stars that I wanted to try to classify:
I used the "ImageKeypoints" function inside of a manipulator in order to extract the XY coordinates of each star in the image. After some adjustments, I was able to successfully accomplish this task.
Coordinate selector:
However, I need the positions in the form of spherical coordinates rather than XY. Thus, I made a function that would linearly scale the coordinates into the right form.
Conversion function:
As you probably guessed, the conversion caused heavy amounts of distortion. As a result, the classifier did not classify the points accurately. While I may not have succeeded, I think that there definitely is a way of converting the points in an accurate manner. One of the areas that I was looking into are map projections since the conversions are quite similar. In the future, I definitely plan on working on the image processing, and I hope to develop an accurate way of classifying the stars in an image.

Final Results

Conclusion

When testing the accuracy of the classifier, I found that it generally works quite well. However, there are some special cases that can confuse the classifier. For example, a set of relative positions that includes stars not included in the 300 brightest stars will not yield accurate results. Another case that leads to inaccuracy would be choosing an narrow area of stars in the sky. While the project is quite good as it stands, there are still issues that need to be resolved and some promising possibilities for expansion.

Acknowledgements

I would like to thank my amazing mentor, Arben Kalziqi, for supporting me with this project throughout its entire development. In addition, I would like to thank the Wolfram Summer Camp staff for organizing such a wonderful program this summer.