0

My problems consists of the following: I am given two pairs angles (in spherical coordinates) which consists of two parts--an azimuth and a colatitude angle. If we extend both angles (thereby increasing their respective radii) infinitely to make a long line pointing in the direction given by the pair of angles, then my goal is to determine

  1. if they intersect or extremely close to one another and
  2. where exactly they intersect.

Currently, I have tried several methods:

  1. The most obvious one is to iteratively compare each radii until there is either a match or a small enough distance between the two. (When I say compare each radii, I am referring to converting each spherical coordinate into Cartesian and then finding the euclidean distance between the two). However, this runtime is $O(n^{2})$, which is extremely slow if I am trying to scale this program

  2. The second most obvious method is to use the optimization package to find this distance. Unfortunately, I cannot the optimization package iteratively and after one instance the optimization algorithm repeats the same answer, which is not useful.

  3. The least obvious method is to directly calculate (using calculus) the exact radii from the angles. While this is fast method, it is not extremely accurate.

Note: while it might seem simple that the intersection is always at the zero-origin (0,0,0), this is not ALWAYS the case. Some points never intersect.

Code for Method (1)

def match1(azimuth_recon_1,colatitude_recon_1,azimuth_recon_2, colatitude_recon_2,centroid_1,centroid_2 ):


   # Constants: tolerance factor and extremely large distance
    tol = 3e-2
    prevDist = 99999999

    # Initialize a list of radii to loop through
    # Checking iteravely for a solution
    for r1 in list(np.arange(0,5,tol)):
        for r2 in list(np.arange(0,5,tol)):

            # Get the estimates
            estimate_1 = np.array(spher2cart(r1,azimuth_recon_1,colatitude_recon_1)) + np.array(centroid_1)
            estimate_2 = np.array(spher2cart(r2,azimuth_recon_2,colatitude_recon_2))+ np.array(centroid_2)

            # Calculate the euclidean distance between them
            dist = np.array(np.sqrt(np.einsum('i...,i...', (estimate_1 - estimate_2), (estimate_1 - estimate_2)))[:,np.newaxis])

        # Compare the distance to this tolerance
            if dist < tol: 
                if dist == 0:
                    return estimate_1, [], True
                else:
                    return estimate_1, estimate_2, False

            ## If the distance is too big break out of the loop
            if dist > prevDist:
                prevDist = 9999999
                break
            prevDist = dist

    return [], [], False

Code for Method (3)

def match2(azimuth_recon_1,colatitude_recon_1,azimuth_recon_2, colatitude_recon_2,centriod_1,centroid_2):

   # Set a Tolerance factor
    tol = 3e-2

    def calculate_radius_2(azimuth_1,colatitude_1,azimuth_2,colatitude_2):

        """Return radius 2 using both pairs of angles (azimuth and colatitude). Equation is provided in the document"""

        return 1/((1-(math.sin(azimuth_1)*math.sin(azimuth_2)*math.cos(colatitude_1-colatitude_2))
        +math.cos(azimuth_1)*math.cos(azimuth_2))**2)

    def calculate_radius_1(radius_2,azimuth_1,colatitude_1,azimuth_2,colatitude_2):

        """Returns radius 1 using both pairs of angles (azimuth and colatitude) and radius 2. 
    Equation provided in document"""

        return (radius_2)*((math.sin(azimuth_1)*math.sin(azimuth_2)*math.cos(colatitude_1-colatitude_2))
        +math.cos(azimuth_1)*math.cos(azimuth_2))

    # Compute radius 2
    radius_2 = calculate_radius_2(azimuth_recon_1,colatitude_recon_1,azimuth_recon_2,colatitude_recon_2)

    #Compute radius 1
    radius_1 = calculate_radius_1(radius_2,azimuth_recon_1,colatitude_recon_1,azimuth_recon_2,colatitude_recon_2)

    # Get the estimates
    estimate_1 = np.array(spher2cart(radius_1,azimuth_recon_1,colatitude_recon_1))+ np.array(centroid_1)
    estimate_2 = np.array(spher2cart(radius_2,azimuth_recon_2,colatitude_recon_2))+ np.array(centroid_2) 

    # Calculate the euclidean distance between them
    dist = np.array(np.sqrt(np.einsum('i...,i...', (estimate_1 - estimate_2), (estimate_1 - estimate_2)))[:,np.newaxis])

    # Compare the distance to this tolerance
    if dist < tol: 
        if dist == 0:
            return estimate_1, [], True
        else:
            return estimate_1, estimate_2, False
    else:
        return [], [], False

My question is two-fold:

  1. Is there a faster and more accurate way to find the radii for both points?

  2. If so, how do I do it?

EDIT: I am thinking about just creating two numpy arrays of the two radii and then comparing them via numpy boolean logic. However, I would still be comparing them iteratively. Is there is a faster way to perform this comparison?

Artemis F
  • 72
  • 3
  • 4
  • 19
  • I don't understand the intersection point is always (0,0,0). can you explain what you mean by "If we extend both angles (thereby increasing their respective radii) infinitely" – Grigory Ilizirov Apr 22 '19 at 20:27
  • Ahhh, sorry, I should've been clearer. Each pair of angles gives us a direction. So, when I say "extend the both angles (thereby increasing their respective radii) infinitely," I am basically saying to make a long line pointing in the direction given by the pair of angles. – Artemis F Apr 22 '19 at 20:58
  • Also the intersection point is not always at point (0,0,0). Some pairs of angles never intersect or are ever close to one another – Artemis F Apr 22 '19 at 21:02
  • That long line origin is (0,0,0) ? – Grigory Ilizirov Apr 22 '19 at 22:18
  • No, the origin of the line (for angle 1) is some pre-determined point (centroid_1: x1,y1,z1)--which I edited in the code above--where x1, y1, and z1 are > 0. Similarly, the origin of the line (for angle 2) is some pre-determined point (centroid_2:x2,y2,z2) where x2, y2, and z2 are > 0 – Artemis F Apr 22 '19 at 22:57

1 Answers1

1

Use a kd-tree for such situations. It will easily look up the minimal distance:

def match(azimuth_recon_1,colatitude_recon_1,azimuth_recon_2, colatitude_recon_2,centriod_1,centroid_2):

    cartesian_1 = np.array([np.cos(azimuth_recon_1)*np.sin(colatitude_recon_1),np.sin(azimuth_recon_1)*np.sin(colatitude_recon_1),np.cos(colatitude_recon_1)]) #[np.newaxis,:]
    cartesian_2 = np.array([np.cos(azimuth_recon_2)*np.sin(colatitude_recon_2),np.sin(azimuth_recon_2)*np.sin(colatitude_recon_2),np.cos(colatitude_recon_2)]) #[np.newaxis,:]

    # Re-center them via adding the centroid
    estimate_1 = r1*cartesian_1.T + np.array(centroid_1)[np.newaxis,:] 
    estimate_2 = r2*cartesian_2.T + np.array(centroid_2)[np.newaxis,:]

    # Add them to the output list
    n = estimate_1.shape[0]
    outputs_list_1.append(estimate_1)
    outputs_list_2.append(estimate_2)

    # Reshape them so that they are in proper format
    a = np.array(outputs_list_1).reshape(len(two_pair_mic_list)*n,3)
    b = np.array(outputs_list_2).reshape(len(two_pair_mic_list)*n,3)

    # Get the difference
    c = a - b

    # Put into a KDtree
    tree = spatial.KDTree(c)

    # Find the indices where the radius (distance between the points) is 3e-3 or less 
    indices = tree.query_ball_tree(3e-3)

This will output a list of the indices where the distance is 3e-3 or less. Now all you will have to do is use the list of indices with the estimate list to find the exact points. And there you have it, this will save you a lot of time and space!

Artemis F
  • 72
  • 3
  • 4
  • 19