Python function for indexing a matrix with two vectors - python

Suppose we have a matrix M and two vectors a and b.
Is there a function in Python that returns:
x = f(M,a,b) such that x(i) = M(a(i),b(i))?
This is of course assuming that the elements in a and b do not exceed the size of M.

def f(M, a, b):
return [M[xa, xb] for xa, xb in zip(a, b)]
is close to what you ask for.
You didn't specify whether a and b are the same length or what to do if they aren't -- zip, which I'm using here, will just stop when the shorter one of them is exhausted (if their lengths differ).
I'm also assuming that by x(i) you mean x[i] (&c) -- the difference between square brackets (indexing) and ordinary parentheses (function call) is crucial in Python, but thinking in terms of function calls (which is what your Q as posted clearly specifies) makes no sense whatsoever.

Related

efficient way to calculate all possible multiplication elements inside a vector in numpy

Is there any efficient way of doing the following:
Assume I have a vector A of length n, I want to calculate a second vector B, where
B[i] = A[0] * A[1] * .. *A[i-1] * A[i+1] *..*A[n-1]
i.e., B[i] is the multipication of all elements in A except for the i'th elmenet.
Initially, I thought of doing something like:
C = np.prod(A)
B = C/A
But, then I have a problem when I have an element of A which is equal to zero. Of course, I can
find out if I have one zero and then immediately set B to be the all-zero vector except for that
single zero and to put there the multiple of the rest of A and in the case of more than 1 zero to zero out B completely. But this becomes a little cumbersome when I want to do that operation for every row inside a matrix and not just for a single vector.
Of course, I can do it in a loop but I was wondering if there is a more efficient way?
You could slice up to (but not including) i, then from i+1 and on. Concatenate those slices together and multiply.
np.prod(np.concatenate([a[:i], a[i+1:]]))
A possible one liner using np.eye, np.tile and np.prod:
np.prod(np.tile(A, (A.size, 1))[(1 - np.eye(A.size)).astype(bool)].reshape(A.size, -1), axis=1)

Conditional Piecewise Function

This might be easier to explain by providing my attempt so far, then my intent+comments.
import numpy as np
from numpy import sqrt, arcsin, arcsinh
# Returns roots of quadratic (or lack of)
def roots(a, b, c):
b24ac = b*b - 4*a*c
if a == 0 or b24ac < 0:
return np.nan, np.nan
else:
l = (-b - sqrt(b24ac))/(2*a)
r = (-b + sqrt(b24ac))/(2*a)
if l > r:
l, r = r, l
return l, r
# Some numeric functions
def pw1(z, a, b, c):
return -arcsin((2*a*z+b)/sqrt(b*b - 4*a*c))/sqrt(-a)
def pw2(z, a, b, c):
return arcsinh((2*a*z+b)/sqrt(4*a*c - b*b))/sqrt(a)
# Function incorporating above definitions w/ conditions/domains
def func(z, a, b, c):
b24ac = b*b - 4*a*c
l, r = roots(*abc)
conditions = [(b24ac > 0 and a < 0) and (l < z and z < r),
(b24ac < 0 and a > 0)]
choices = [pw1(z, a, b, c),
pw2(z, a, b, c)]
return np.select(conditions, choices)
This is my attempt at creating a python function that is a conditional piecewise function. For the mathematically curious, this is a portion of a full definition of the integral of $[ax^2+bx+c]^{-1/2}$. The necessary specifics are that I need a function that is conditional on the domain AND other parameters. I've looked into numpy's piecewise and select functions. Piecewise, for its condition list, only accepts logic on the domain (not the parameters). Unless I'm missing something, this seems like it won't work for me. Select has given me the most success. The only issues I've had are it not evaluating domain conditions, over its domain:
---> conditions = [(b24ac > 0 and a < 0) and (l < z and z < r),
(b24ac < 0 and a > 0)]
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Lastly, it evaluates all choices (which are results as opposed to the functions that you give piecewise), then selects the one per the conditions list to return (commenting out the and (l < z... conditions):
c:\program files\python36\lib\site-packages\ipykernel_launcher.py:6:
RuntimeWarning: invalid value encountered in sqrt
I started this weeks ago with a bunch of elif statements. This worked only for floats, not arrays. I'm using numpy, and having this function (or any really) evaluate over a domain is what I'm after. I then learned about piecewise and select and began playing with these, being from numpy.
I'd really like a pythonic way to do this. One that evaluates over numpy arrays, and only for the desired condition/domain. So, behaves like piecewise but has versatile conditioning like select. Any help and suggestions are greatly appreciated! Thanks.
The b24ac identifier is certainly descriptive,
but most folks would probably name it discriminant.
use your own
You are complaining that you know how to compute the desired result,
but the numpy tools at hand don't seem a good fit.
So write your own function fn and use .apply(fn), or nditer.
Notice that you can arrange for all needed arguments to appear in extra columns,
and have the function handle a row at a time.
I heard no concerns about speed,
which would be the usual argument against such a custom function.
use np.piecewise
I agree with you that piecewise() appears to be appropriate to your needs.
The aspect that seemed missing was tailoring your functions with four parameters
before passing them to piecewise.
A good match for that need would be functools.partial().

Write a function with NumPy to calculate integral with a specific tolerance

I want to write a custom function to integrate an expression (python or lambda function) numerically with a specific tolerance. I know with scipy.integrate.quad one can simply change the epsabs but I want to write the function myself using numpy.
From this blogpost I know the function:
def integrate(f, a, b, N):
x = np.linspace(a+(b-a)/(2*N), b-(b-a)/(2*N), N)
fx = f(x)
area = np.sum(fx)*(b-a)/N
return area
gives me the numerical integration with N segments. How can I write another function or extend this to take a tol input and increase N until the difference between the two subsequent calculations is smaller than the given tolerance?
Using the function you have, one can start with a reasonable N, for example 5, and keep doubling the number until the required precision is reached.
def integrate_tol(f, a, b, tol):
N = 5
old_integral = integrate(f, a, b, N)
while True:
N *= 2
new_integral = integrate(f, a, b, N)
if np.abs(old_integral - new_integral) < tol:
return (4*new_integral - old_integral)/3
old_integral = new_integral
A simple test:
f = lambda x: np.exp(x)
print(integrate_tol(f, -1, 1, 1e-9))
print(np.exp(1)-np.exp(-1)) # exact value for comparison
prints
2.3504023872876028
2.3504023872876028
There is no guarantee that the error is indeed less than tol (but then again, scipy.quad does not guarantee that either). In practice, the error will be much smaller than tol, because of the trick I used, called Richardson extrapolation: the return value (4*new_integral - old_integral)/3 is in general much more accurate than either new or old approximations themselves. (Explanation: since integrate uses the midpoint rule, each doubling of N reduces the error by the factor of about 4. So, taking the combination 4*new_integral - old_integral nearly cancels out the residual error in both of those results.)
(Remark: in the while loop it's unadvisable to start with N=1; it probably will not be enough, and there's higher risk of stopping too early because of some numerical coincidence, e.g., the function being zero in a bunch of places.)

Computing the cross ratio

I want to write a function in python to compute the cross ratio of four projective points, and I am wondering whether there is an elegant and succinct implementation to handle infinite cases.
A naive implementation of the cross ratio looks like this:
ratio = lambda a,b,c: (a-c)/(b-c)
cross_ratio = lambda a,b,c,d: ratio(a,b,c)/ratio(a,b,d)
But this fails when one of the inputs is Infinity. This should not happen, but rather we would like the infinities to "cancel out each other" and give us a simple ratio.
For example, the cross ratio of Infinity, 0, 1, -1 should be -1.
Also, I would like to handle points expressed as a ratio of two numbers. Thus (1 1)would be the number 1, while (1,0)would represent Infinity, etc.
I could always fall back to a definition by cases and make do with it, but I feel this may be a good opportunity to learn good design.
I am using Python 2.7 and the Sagemath module. Any advices on how to implement this?
I'd try this:
def det2(a, b): return a[0]*b[1] - a[1]*b[0]
def cr2(a, b, c, d): return vector([det2(a,c)*det2(b,d), det2(a,d)*det2(b,c)])
This would use homogeneous coordinates on input, so you'd inout two-element vectors. It would also return its result in homogeneous coordinates, as a two-element vector, so you could get a clean description of infinite cross ratio. If you need the result as an element of some field instead, just use division instead of the vector constructor:
def cr2(a, b, c, d): return (det2(a,c)*det2(b,d))/(det2(a,d)*det2(b,c))
I added the suffix 2 to my formulas because personally I often need the cross ratio of four collinear points in the plane. In that case, I'd use
def det3(a, b, c):
return matrix([a,b,c]).det() # Or spell this out, if you prefer
def cr3(a, b, c, d, x):
return vector([det3(a,c,x)*det3(b,d,x), det3(a,d,x)* det3(b,c,x)])
Now let x be any point not collinear with a,b,c,d and you get the cross ratio of these four points. Or more generally, if a,b,c,d are not collinear, you get the cross ratio of the four lines connecting these to x, which can be useful for a number of scenarios, many of them involving conics.
The best is to work with the projective line.
The documentation here contains useful hints:
http://doc.sagemath.org/html/en/reference/schemes/sage/schemes/projective/projective_space.html
Here is an implementation of the cross-ratio, with examples.
sage: P = ProjectiveSpace(1, QQ)
sage: oo, zero, one = P(1, 0), P(0, 1), P(1, 1)
sage: tm = P.point_transformation_matrix
sage: def cross_ratio(a, b, c, d):
....: a, b, c, d = P(a), P(b), P(c), P(d)
....: m = tm([a, b, c], [oo, zero, one])
....: return P(list(m*vector(list(d))))
....:
sage: cross_ratio(oo, zero, one, 1/2)
(1/2 : 1)
sage: cross_ratio(1, 2, 3, 4)
(4/3 : 1)

Speed up double for loop in numpy

I currently have the following double loop in my Python code:
for i in range(a):
for j in range(b):
A[:,i]*=B[j][:,C[i,j]]
(A is a float matrix. B is a list of float matrices. C is a matrix of integers. By matrices I mean m x n np.arrays.
To be precise, the sizes are: A: mxa B: b matrices of size mxl (with l different for each matrix) C: axb. Here m is very large, a is very large, b is small, the l's are even smaller than b
)
I tried to speed it up by doing
for j in range(b):
A[:,:]*=B[j][:,C[:,j]]
but surprisingly to me this performed worse.
More precisely, this did improve performance for small values of m and a (the "large" numbers), but from m=7000,a=700 onwards the first appraoch is roughly twice as fast.
Is there anything else I can do?
Maybe I could parallelize? But I don't really know how.
(I am not committed to either Python 2 or 3)
Here's a vectorized approach assuming B as a list of arrays that are of the same shape -
# Convert B to a 3D array
B_arr = np.asarray(B)
# Use advanced indexing to index into the last axis of B array with C
# and then do product-reduction along the second axis.
# Finally, we perform elementwise multiplication with A
A *= B_arr[np.arange(B_arr.shape[0]),:,C].prod(1).T
For cases with smaller a, we could run a loop that iterates through the length of a instead. Also, for more performance, it might be a better idea to store those elements into a separate 2D array instead and perform the elementwise multiplication only once after we get out of the loop.
Thus, we would have an alternative implementation like so -
range_arr = np.arange(B_arr.shape[0])
out = np.empty_like(A)
for i in range(a):
out[:,i] = B_arr[range_arr,:,C[i,:]].prod(0)
A *= out

Categories