0

I have a function which I would like to convert so that i can use with awkward array 1.

Function following which used to work for float but not for awkward arrays for the known reasons.

def Phi_mpi_pi(x):
    kPI=(3.14159265)
    kTWOPI = 2 * kPI

    #while ((x.any() >= kPI).any()): x = x - kTWOPI;                                                                                                                                                        
    #while ((x.any() < -kPI).any()): x = x + kTWOPI;                                                                                                                                                        
    while ((x >= kPI)): x = x - kTWOPI;
    while ((x < -kPI)): x = x + kTWOPI;
    return x;

I tried to convert it into numpy/awkward compatible form and new function look like

def Phi_mpi_pi(x):
    kPI=numpy.array(3.14159265)
    kPI = kPI.repeat(len(x))
    kTWOPI = 2 * kPI
    while ((x >= kPI)): x = x - kTWOPI;
    while ((x < -kPI)): x = x + kTWOPI;
    return x;

This function remains stuck in the while loop forever, I couldn't find a way to debug it.

Task of the function is to keep the values in an awkward array between +- kPI but this logic does not give the desired results.

e.g.

x=ak.Array([[0.7999999999999998, 1.0, -1.3], [], [-1.4], [-1.8000000000000003, -6.1000000000000005, -1.6000000000000005], [-4.6]])

However ((x < -kPI)) this give desired output.

>>> ak.to_list(x <= -kPI)
    [[False, False, False], [], [False], [False, True, False], [True]]

but not the function

the desired output should be b/w +- kPI based on the logic of while loop, is there something straightforward or suggestion which can be used?

Raman Khurana
  • 125
  • 1
  • 7

2 Answers2

0

I don't quite understand what you want, but this might at least help you debug:

import numpy
import awkward1 as ak

def Phi_mpi_pi(x):
    kPI=numpy.array(3.14159265)
    kPI = kPI.repeat(len(x))
    kTWOPI = 2 * kPI
    print("kPI =", kPI)
    print("kTWOPI =", kTWOPI)
    while ((x >= kPI)):
        print(x)
        x = x - kTWOPI
    while ((x < -kPI)):
        print(x)
        x = x + kTWOPI
    return x

x=ak.Array([[0.7999999999999998, 1.0, -1.3], [], [-1.4], [-1.8000000000000003, -6.1000000000000005, -1.6000000000000005], [-4.6]])

print(Phi_mpi_pi(x))

It's too long for a comment, so I post it as an answer, even though of course it doesn't directly represent a solution.

Jesper
  • 1,611
  • 13
  • 10
  • Thanks for suggestion, but I have already tried this but couldn't help myself with this. Let me try to clarify further. I want to transform the awkward array of float numbers b/w -3.14 to +3.14. if the number is small or bigger than these bounds then i need to add the quantity 2*3.14. This is ok for a number but for an awkward array I am not sure how if will treat the condition. I can't use any or all functions becuase I want to transform only those numbers in the awkward array which are out of these bounds. – Raman Khurana Oct 09 '20 at 22:17
  • Ah, you want to adjust each single angular value in the awkard array independently so they are each between -π and +π but still the same angle (points in the same direction)? If so, I understand the problem now. – Jesper Oct 10 '20 at 11:45
  • So you want to independently apply the original float version of Phi_mpi_pi() to each individual value in the awkward array. In other words, a map() of Phi_mpi_pi() to each value. Let's see how that is done... Will investigate. – Jesper Oct 10 '20 at 11:47
  • Okay, added [another answer](https://stackoverflow.com/a/64293815/1629102) which I think does what you want. – Jesper Oct 10 '20 at 14:18
  • Thanks Jesper. I think I found the issue in my original version. I create KPi using kPI=numpy.array(3.14159265) kPI = kPI.repeat(len(x)) However I must use the same structure as the original awkward array in order to avoid the problem i noticed. But I will still use the while statement. I liked the idea of using mod instead of while, which i believe will be faster w.r.t my solution Thanks. Let me give it a try. – Raman Khurana Oct 10 '20 at 19:55
  • @RamanKhurana I don't think your version will work. The logic is not right, because you add or subtract from all values at the same time. That is the problem with it. My solution doesn't do that. – Jesper Oct 10 '20 at 20:24
  • The point is that you have to adjust every single value individually. The algorithm simply cannot do that if it works on all values at the same time, so that algorithm will only work if you apply it to each value individually. In that case you will need it to create a numpy ufunc (universal function) from it. I tried to do that (with numpy.frompyfunc(Phi_mpi_pi, 1, 1)) but could not make it work. Then I came up with the much simpler and likely much faster version in the other answer, and it works perfectly and the included tests even show that the results are correct. – Jesper Oct 11 '20 at 12:01
0

Okay, got it. You want to adjust every single scalar value in x (which are all angles) to be between -π and π.

You can do it like this:

def Phi_mpi_pi(x):
    y = numpy.add(x, numpy.pi)
    y = numpy.mod(y, 2*numpy.pi)
    y = numpy.subtract(y, numpy.pi)
    return y

Or, more terse and much less readable:

def Phi_mpi_pi(x):
    return numpy.subtract(numpy.mod(numpy.add(x, numpy.pi), 2*numpy.pi), numpy.pi)

What it does is this:

  1. Add π to all angles (so they point in the opposite direction).
  2. Take modulo 2π on all angles so they are all from 0 to 2π (2π not included).
  3. Subtract π from all the angles again (so they point in the right direction again). Now they are all from -π to +π (+π not included).

Test:

x = ak.Array([[0.3, 3.1, numpy.pi, -numpy.pi, -4 * numpy.pi,
               200 * numpy.pi, 2 * numpy.pi, -400 * numpy.pi], 
              [], [-1.4], [-1.8, -6, -1.6], [-4.6]])
y = Phi_mpi_pi(x)
print("Type of result:", type(y))
print("Result =", y)

# Check that the resulting range of each value is correct.
range_is_correct = (ak.all(y >= -numpy.pi) and ak.all(y < numpy.pi))

# Calculate the factors of the 2π adjustments.
factors = (x - y) / (2 * numpy.pi)
print("2π factors=", factors)

# Test that all factors of the 2π adjustmenst are approximate integers.
adjustments_are_correct = ak.all(numpy.abs(numpy.mod(factors, 1)) < 1e-15)

# Test that all values are correct, given the test input.
print("Result is correct:", range_is_correct and adjustments_are_correct)

gives this output:

Type of result: <class 'awkward1.highlevel.Array'>
Result = [[0.3, 3.1, -3.14, -3.14, 0, 1.78e-14, 0, 4.62e-14, ... [-1.8, 0.283, -1.6], [1.68]]
2π factors= [[2.65e-17, 7.07e-17, 1, 0, -2, 100, 1, -200], [], [0], [0, -1, 0], [-1]]
Result is correct: True

which proves that the operation was correctly performed with the specifically used test data.

Was that what you wanted?

Jesper
  • 1,611
  • 13
  • 10