📚 The CoCalc Library - books, templates and other resources
License: OTHER
""" DarkWorldsMetricMountianOsteric.py1Custom evaluation metric for the 'Observing Dark Worlds' competition.23[Description of metric, or reference to documentation.]45Update: Made for the training set only so users can check there results from the training c67@Author: David Harvey8Created: 22 August 20129"""1011import numpy as np12import math as mt13import itertools as it14import csv as c15import getopt as gt16import sys as sys17import argparse as ap18import string as st19import random as rd2021def calc_delta_r(x_predicted,y_predicted,x_true,y_true):22""" Compute the scalar distance between predicted halo centers23and the true halo centers. Predictions are matched to the closest24halo center.25Notes: It takes in the predicted and true positions, and then loops over each possible configuration and finds the most optimal one.26Arguments:27x_predicted, y_predicted: vector for predicted x- and y-positions (1 to 3 elements)28x_true, y_true: vector for known x- and y-positions (1 to 3 elements)29Returns:30radial_distance: vector containing the scalar distances between the predicted halo centres and the true halo centres (1 to 3 elements)31true_halo_idexes: vector containing indexes of the input true halos which matches the predicted halo indexes (1 to 3 elements)32measured_halo_indexes: vector containing indexes of the predicted halo position with the reference to the true halo position.33e.g if true_halo_indexes=[0,1] and measured_halo_indexes=[1,0] then the first x,y coordinates of the true halo position matches the second input of the predicted x,y coordinates.34"""3536num_halos=len(x_true) #Only works for number of halos > 137num_configurations=mt.factorial(num_halos) #The number of possible different comb38configurations=np.zeros([num_halos,num_configurations],int) #The array of combinations39#I will pass back40distances = np.zeros([num_configurations],float) #The array of the distances41#for all possible combinations4243radial_distance=[] #The vector of distances44#I will pass back4546#Pick a combination of true and predicted47a=['01','012'] #Input for the permutatiosn, 01 number halos or 01248count=0 #For the index of the distances array49true_halo_indexes=[] #The tuples which will show the order of halos picked50predicted_halo_indexes=[]51distances_perm=np.zeros([num_configurations,num_halos],float) #The distance between each52#true and predicted53#halo for every comb54true_halo_indexes_perm=[] #log of all the permutations of true halos used55predicted_halo_indexes_perm=[] #log of all the predicted permutations5657for perm in it.permutations(a[num_halos-2],num_halos):58which_true_halos=[]59which_predicted_halos=[]60for j in range(num_halos): #loop through all the true halos with the6162distances_perm[count,j]=np.sqrt((x_true[j]-x_predicted[int(perm[j])])**2\63+(y_true[j]-y_predicted[int(perm[j])])**2)64#This array logs the distance between true and65#predicted halo for ALL configurations6667which_true_halos.append(j) #log the order in which I try each true halo68which_predicted_halos.append(int(perm[j])) #log the order in which I true69#each predicted halo70true_halo_indexes_perm.append(which_true_halos) #this is a tuple of tuples of71#all of thifferent config72#true halo indexes73predicted_halo_indexes_perm.append(which_predicted_halos)7475distances[count]=sum(distances_perm[count,0::]) #Find what the total distances76#are for each configuration77count=count+17879config = np.where(distances == min(distances))[0][0] #The configuration used is the one80#which has the smallest distance81radial_distance.append(distances_perm[config,0::]) #Find the tuple of distances that82#correspond to this smallest distance83true_halo_indexes=true_halo_indexes_perm[config] #Find the tuple of the index which refers84#to the smallest distance85predicted_halo_indexes=predicted_halo_indexes_perm[config]8687return radial_distance,true_halo_indexes,predicted_halo_indexes888990def calc_theta(x_predicted, y_predicted, x_true, y_true, x_ref, y_ref):91""" Calculate the angle the predicted position and the true position, where the zero degree corresponds to the line joing the true halo position and the reference point given.92Arguments:93x_predicted, y_predicted: vector for predicted x- and y-positions (1 to 3 elements)94x_true, y_true: vector for known x- and y-positions (1 to 3 elements)95Note that the input of these are matched up so that the first elements of each96vector are associated with one another97x_ref, y_ref: scalars of the x,y coordinate of reference point98Returns:99Theta: A vector containing the angles of the predicted halo w.r.t the true halo100with the vector joining the reference point and the halo as the zero line.101"""102103num_halos=len(x_predicted)104theta=np.zeros([num_halos+1],float) #Set up the array which will pass back the values105phi = np.zeros([num_halos],float)106107psi = np.arctan( (y_true-y_ref)/(x_true-x_ref) )108109110# Angle at which the halo is at111#with respect to the reference point112phi[x_true != x_ref] = np.arctan((y_predicted[x_true != x_predicted]-\113y_true[x_true != x_predicted])\114/(x_predicted[x_true != x_predicted]-\115x_true[x_true != x_predicted])) # Angle of the estimate116#wrt true halo centre117118#Before finding the angle with the zero line as the line joiing the halo and the reference119#point I need to convert the angle produced by Python to an angle between 0 and 2pi120phi =convert_to_360(phi, x_predicted-x_true,\121y_predicted-y_true)122psi = convert_to_360(psi, x_true-x_ref,\123y_true-y_ref)124theta = phi-psi #The angle with the baseline as the line joing the ref and the halo125126127theta[theta< 0.0]=theta[theta< 0.0]+2.0*mt.pi #If the angle of the true pos wrt the ref is128#greater than the angle of predicted pos129#and the true pos then add 2pi130return theta131132133def convert_to_360(angle, x_in, y_in):134""" Convert the given angle to the true angle in the range 0:2pi135Arguments:136angle:137x_in, y_in: the x and y coordinates used to determine the quartile138the coordinate lies in so to add of pi or 2pi139Returns:140theta: the angle in the range 0:2pi141"""142n = len(x_in)143for i in range(n):144if x_in[i] < 0 and y_in[i] > 0:145angle[i] = angle[i]+mt.pi146elif x_in[i] < 0 and y_in[i] < 0:147angle[i] = angle[i]+mt.pi148elif x_in[i] > 0 and y_in[i] < 0:149angle[i] = angle[i]+2.0*mt.pi150elif x_in[i] == 0 and y_in[i] == 0:151angle[i] = 0152elif x_in[i] == 0 and y_in[i] > 0:153angle[i] = mt.pi/2.154elif x_in[i] < 0 and y_in[i] == 0:155angle[i] = mt.pi156elif x_in[i] == 0 and y_in[i] < 0:157angle[i] = 3.*mt.pi/2.158159160161return angle162163def get_ref(x_halo,y_halo,weight):164""" Gets the reference point of the system of halos by weighted averaging the x and y165coordinates.166Arguments:167x_halo, y_halo: Vector num_halos referring to the coordinates of the halos168weight: the weight which will be assigned to the position of the halo169num_halos: number of halos in the system170Returns:171x_ref, y_ref: The coordinates of the reference point for the metric172"""173174175#Find the weighted average of the x and y coordinates176x_ref = np.sum([x_halo*weight])/np.sum([weight])177y_ref = np.sum([y_halo*weight])/np.sum([weight])178179180return x_ref,y_ref181182183def main_score( nhalo_all, x_true_all, y_true_all, x_ref_all, y_ref_all, sky_prediction):184"""abstracts the score from the old command-line interface.185sky_prediction is a dx2 array of predicted x,y positions186187-camdp"""188189r=np.array([],dtype=float) # The array which I will log all the calculated radial distances190angle=np.array([],dtype=float) #The array which I will log all the calculated angles191#Load in the sky_ids from the true192num_halos_total=0 #Keep track of how many halos are input into the metric193194195196for selectskyinsolutions, sky in enumerate(sky_prediction): #Loop through each line in result.csv and analyse each one197198199nhalo=int(nhalo_all[selectskyinsolutions])#How many halos in the200#selected sky?201x_true=x_true_all[selectskyinsolutions][0:nhalo]202y_true=y_true_all[selectskyinsolutions][0:nhalo]203204x_predicted=np.array([],dtype=float)205y_predicted=np.array([],dtype=float)206for i in range(nhalo):207x_predicted=np.append(x_predicted,float(sky[0])) #get the predicted values208y_predicted=np.append(y_predicted,float(sky[1]))209#The solution file for the test data provides masses210#to calculate the centre of mass where as the Training_halo.csv211#direct provides x_ref y_ref. So in the case of test data212#we need to calculate the ref point from the masses using213#Get_ref()214215x_ref=x_ref_all[selectskyinsolutions]216y_ref=y_ref_all[selectskyinsolutions]217218num_halos_total=num_halos_total+nhalo219220221#Single halo case, this needs to be separately calculated since222#x_ref = x_true223if nhalo == 1:224#What is the radial distance between the true and predicted position225r=np.append(r,np.sqrt( (x_predicted-x_true)**2 \226+ (y_predicted-y_true)**2))227#What is the angle between the predicted position and true halo position228if (x_predicted-x_true) != 0:229psi = np.arctan((y_predicted-y_true)/(x_predicted-x_true))230else: psi=0.231theta = convert_to_360([psi], [x_predicted-x_true], [y_predicted-y_true])232angle=np.append(angle,theta)233234235else:236#r_index_index, contains the radial distances of the predicted to237#true positions. These are found by matching up the true halos to238#the predicted halos such that the average of all the radial distances239#is optimal. it also contains indexes of the halos used which are used to240#show which halo has been mathced to which.241242r_index_index = calc_delta_r(x_predicted, y_predicted, x_true, \243y_true)244245r=np.append(r,r_index_index[0][0])246halo_index= r_index_index[1] #The true halos indexes matched with the247predicted_index=r_index_index[2] #predicted halo index248249angle=np.append(angle,calc_theta\250(x_predicted[predicted_index],\251y_predicted[predicted_index],\252x_true[halo_index],\253y_true[halo_index],x_ref,\254y_ref)) # Find the angles of the predicted255#position wrt to the halo and256# add to the vector angle257258259# Find what the average distance the estimate is from the halo position260av_r=sum(r)/len(r)261262#In order to quantify the orientation invariance we will express each angle263# as a vector and find the average vector264#R_bar^2=(1/N Sum^Ncos(theta))^2+(1/N Sum^Nsin(theta))**2265266N = float(num_halos_total)267angle_vec = np.sqrt(( 1.0/N * sum(np.cos(angle)) )**2 + \268( 1.0/N * sum(np.sin(angle)) )**2)269270W1=1./1000. #Weight the av_r such that < 1 is a good score > 1 is not so good.271W2=1.272metric = W1*av_r + W2*angle_vec #Weighted metric, weights TBD273print('Your average distance in pixels you are away from the true halo is', av_r)274print('Your average angular vector is', angle_vec)275print('Your score for the training data is', metric)276return metric277278279def main(user_fname, fname):280""" Script to compute the evaluation metric for the Observing Dark Worlds competition. You can run it on your training data to understand how well you have done with the training data.281"""282283r=np.array([],dtype=float) # The array which I will log all the calculated radial distances284angle=np.array([],dtype=float) #The array which I will log all the calculated angles285#Load in the sky_ids from the true286287true_sky_id=[]288sky_loader = c.reader(open(fname, 'rb')) #Load in the sky_ids from the solution file289for row in sky_loader:290true_sky_id.append(row[0])291292#Load in the true values from the solution file293294nhalo_all=np.loadtxt(fname,usecols=(1,),delimiter=',',skiprows=1)295x_true_all=np.loadtxt(fname,usecols=(4,6,8),delimiter=',',skiprows=1)296y_true_all=np.loadtxt(fname,usecols=(5,7,9),delimiter=',',skiprows=1)297x_ref_all=np.loadtxt(fname,usecols=(2,),delimiter=',',skiprows=1)298y_ref_all=np.loadtxt(fname,usecols=(3,),delimiter=',',skiprows=1)299300301for row in sky_loader:302true_sky_id.append(row[1])303304305306num_halos_total=0 #Keep track of how many halos are input into the metric307308309sky_prediction = c.reader(open(user_fname, 'rb')) #Open the result.csv310311try: #See if the input file from user has a header on it312#with open('JoyceTest/trivialUnitTest_Pred.txt', 'r') as f:313with open(user_fname, 'r') as f:314header = float((f.readline()).split(',')[1]) #try and make where the315#first input would be316#a float, if succeed it317#is not a header318print('THE INPUT FILE DOES NOT APPEAR TO HAVE A HEADER')319except :320print('THE INPUT FILE APPEARS TO HAVE A HEADER, SKIPPING THE FIRST LINE')321skip_header = sky_prediction.next()322323324for sky in sky_prediction: #Loop through each line in result.csv and analyse each one325sky_id = str(sky[0]) #Get the sky_id of the input326does_it_exist=true_sky_id.count(sky_id) #Is the input sky_id327#from user a real one?328329if does_it_exist > 0: #If it does then find the matching solutions to the sky_id330selectskyinsolutions=true_sky_id.index(sky_id)-1331else: #Otherwise exit332print('Sky_id does not exist, formatting problem: ',sky_id)333sys.exit(2)334335336nhalo=int(nhalo_all[selectskyinsolutions])#How many halos in the337#selected sky?338x_true=x_true_all[selectskyinsolutions][0:nhalo]339y_true=y_true_all[selectskyinsolutions][0:nhalo]340341x_predicted=np.array([],dtype=float)342y_predicted=np.array([],dtype=float)343for i in range(nhalo):344x_predicted=np.append(x_predicted,float(sky[2*i+1])) #get the predicted values345y_predicted=np.append(y_predicted,float(sky[2*i+2]))346#The solution file for the test data provides masses347#to calculate the centre of mass where as the Training_halo.csv348#direct provides x_ref y_ref. So in the case of test data349#we need to calculae the ref point from the masses using350#Get_ref()351352x_ref=x_ref_all[selectskyinsolutions]353y_ref=y_ref_all[selectskyinsolutions]354355num_halos_total=num_halos_total+nhalo356357358#Single halo case, this needs to be separately calculated since359#x_ref = x_true360if nhalo == 1:361#What is the radial distance between the true and predicted position362r=np.append(r,np.sqrt( (x_predicted-x_true)**2 \363+ (y_predicted-y_true)**2))364#What is the angle between the predicted position and true halo position365if (x_predicted-x_true) != 0:366psi = np.arctan((y_predicted-y_true)/(x_predicted-x_true))367else: psi=0.368theta = convert_to_360([psi], [x_predicted-x_true], [y_predicted-y_true])369angle=np.append(angle,theta)370371372else:373#r_index_index, contains the radial distances of the predicted to374#true positions. These are found by matching up the true halos to375#the predicted halos such that the average of all the radial distances376#is optimal. it also contains indexes of the halos used which are used to377#show which halo has been mathced to which.378379r_index_index = calc_delta_r(x_predicted, y_predicted, x_true, \380y_true)381382r=np.append(r,r_index_index[0][0])383halo_index= r_index_index[1] #The true halos indexes matched with the384predicted_index=r_index_index[2] #predicted halo index385386angle=np.append(angle,calc_theta\387(x_predicted[predicted_index],\388y_predicted[predicted_index],\389x_true[halo_index],\390y_true[halo_index],x_ref,\391y_ref)) # Find the angles of the predicted392#position wrt to the halo and393# add to the vector angle394395396# Find what the average distance the estimate is from the halo position397av_r=sum(r)/len(r)398399#In order to quantify the orientation invariance we will express each angle400# as a vector and find the average vector401#R_bar^2=(1/N Sum^Ncos(theta))^2+(1/N Sum^Nsin(theta))**2402403N = float(num_halos_total)404angle_vec = np.sqrt(( 1.0/N * sum(np.cos(angle)) )**2 + \405( 1.0/N * sum(np.sin(angle)) )**2)406407W1=1./1000. #Weight the av_r such that < 1 is a good score > 1 is not so good.408W2=1.409metric = W1*av_r + W2*angle_vec #Weighted metric, weights TBD410print('Your average distance in pixels you are away from the true halo is', av_r)411print('Your average angular vector is', angle_vec)412print('Your score for the training data is', metric)413414415if __name__ == "__main__":416#For help just typed 'python DarkWorldsMetric.py -h'417418parser = ap.ArgumentParser(description='Work out the Metric for your input file')419parser.add_argument('inputfile',type=str,nargs=1,help='Input file of halo positions. Needs to be in the format SkyId,halo_x1,haloy1,halox_2,halo_y2,halox3,halo_y3 ')420parser.add_argument('reffile',type=str,nargs=1,help='This should point to Training_halos.csv')421args = parser.parse_args()422423user_fname=args.inputfile[0]424filename = (args.reffile[0]).count('Training_halos.csv')425if filename == 0:426fname=args.reffile[0]+str('Training_halos.csv')427else:428fname=args.reffile[0]429430main(user_fname, fname)431432433434