Python multiprocessing - "outer product" of arguments - multithreading

I'm doing a numerical simulation with multiple input parameters. Some of these parameters are static, while some are arrays of numbers I want to run my function at. For example, I may want to simulate the following set of parameters
a = 1
b = np.arange(1, 11)
c = np.arange(20, 31)
d = 1
Which would mean running
simulate(a = 1, b = 1, c = 20, d = 1)
simulate(a = 1, b = 1, c = 21, d = 1)
...
simulate(a = 1, b = 1, c = 30, d = 1)
simulate(a = 1, b = 2, c = 20, d = 1)
...
i.e. 100 calls to simulate(). I want to use multithreading to speed this up. I tried using multiprocessing's pool.map(), but the requirement for the structure of the input parameters requires instantiating a length-100 array containing lists of a, b, c, and d, e.g. [[1, 1, 20, 1], [1, 1, 21, 1] ...
In actuality I have enough parameters and am varying in enough dimensions that I run out of memory trying to generate the input array for pool.map().
What I'd like is to have a function map_wrapper() such that
a = 1
b = np.arange(1, 11)
c = np.arange(20, 31)
d = 1
map_wrapper(simulation, [a, b, c, d])
Is equivalent to the 100 calls of simulation() listed above, or a way of using map() or similar in the same way.

Here's a minimal example showing how to build a generator that yields args for a multiprocessing function:
import multiprocessing as mp
def gen_args():
for a in range(4):
for b in range(4,8):
for c in range(8,12):
yield (a,b,c)
def foo(a, b, c):
return a + b + c
if __name__ == '__main__':
with mp.Pool() as p:
res = p.starmap(foo, gen_args())
This will generate 64 tasks, but the arguments are calculated on the fly as needed rather than all at once at the beginning. Keep in mind you'll still need the space for the output list. Using the chunksize argument for starmap may or may not improve execution speed (test it both ways to find out), but it will increase memory usage somewhat, as it pulls multiple sets of args at a time for each worker function to work on.

Related

Collect coefficients of non-square matrix in sympy

Given three vectors, a(1,4), b(1, 4), c(4,1). I want to do the following
a = MatrixSymbol('a', 1, 4)
b = MatrixSymbol('a', 1, 4)
c = MatrixSymbol('c', 4, 1)
expr = a*c + b*c
c_coeff = .... # How to get the value of (a+b) here
I tried using coeff and collect but it fails because the C matrix is not square.
If it is linear then
>>> from sympy.solvers.solveset import linear_coeffs
>>> linear_coeffs(expr, c)
[a + b, 0]
note that 0 is the constant term; if you added MatrixSymbol('d', 1, 1) to expr the 2nd element would be d; you named 'b' as 'a' in your example; I named it 'b' to get the output shown above.

Python code keeps getting killed, most likely due to running out of memory, while making a list using iteration

I'm trying to solve problem 31 in ProjectEuler.net and the solution I came up with was to make a list of all possible combinations and then only count the ones that add up to £2. To make a list of all possible combinations this is the code I came up with:
c1p = []
for x in range(201):
c1p.append(x)
c2p = []
for x in range(101):
c2p.append(x * 2)
c5p = []
for x in range(41):
c5p.append(x * 5)
c10p = []
for x in range(21):
c10p.append(x * 10)
c20p = []
for x in range(11):
c20p.append(x * 20)
c50p = []
for x in range(5):
c50p.append(x * 50)
c100p = []
for x in range(3):
c100p.append(x * 100)
counter = 1 #As a single £2 coin is already equal to £2
total_combs = [[a, b, c, d, e, f, g] for a in c1p for b in c2p for c in c5p for d in c10p for e in c20p for f in c50p for g in c100p
The way this is currently written, combinations over £2 will keep getting added to the list even though they're not necessary, and stopping this may help it so I don't run out of memory but I can't figure out how to do it.

How to check a list of lists against a list of lists and count their overlaps? (Python)

I have one list a containing 100 lists and one list x containing 4 lists (all of equal length). I want to test the lists in a against those in x. My goal is to find out how often numbers in a "touch" those in x. Stated differently, all the lists are points on a line and the lines in a should not touch (or cross) those in x.
EDIT
In the code, I am testing each line in a (e.g. a1, a2 ... a100) first against x1, then against x2, x3 and x4. A condition and a counter check whether the a's touch the x's. Note: I am not interested in counting how many items in a1, for example, touch x1. Once a1 and x1 touch, I count that and can move on to a2, and so on.
However, the counter does not properly update. It seems that it does not tests a against all x. Any suggestions on how to solve this? Here is my code.
EDIT
I have updated the code so that the problem is easier to replicate.
x = [[10, 11, 12], [14, 15, 16]]
a = [[11, 10, 12], [15, 17, 20], [11, 14, 16]]
def touch(e, f):
e = np.array(e)
f = np.array(f)
lastitems = []
counter = 0
for lst in f:
if np.all(e < lst): # This is the condition
lastitems.append(lst[-1]) # This allows checking the end values
else:
counter += 1
c = counter
return c
touch = touch(x, a)
print(touch)
The result I get is:
2
But I expect this:
1
2
I'm unsure of what exactly is the result you expect, your example and description are still not clear. Anyway, this is what I guess you want. If you want more details, you can uncomment some lines i.e. those with #
i = 0
for j in x:
print("")
#print(j)
counter = 0
for k in a:
inters = set(j).intersection(k)
#print(k)
#print(inters)
if inters:
counter += 1
#print("yes", counter)
#else:
#print("nope", counter)
print(i, counter)
i += 1
which prints
0 2
1 2

Printing fibonacci series using lists in python

I am relatively a noobie in programming and trying to learn python. I was trying to implement a Fibonacci series into a list within 10.
fibo= [0,1]
for k in range(11):
i= fibo[-1]
j = fibo[-2]
k= fibo[i]+fibo[j]
fibo.append(k)
k=+1
print(fibo)
Not sure what I did wrong? Any help is really appreciated!
Output:
[0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
You can use this code to print fibonacci series upto N.
N = int(input()) # Length of fibonacci series
fibo = [0, 1]
a = fibo[0]
b = fibo[1]
for each in range(2, N):
c = a + b
a, b = b, c
fibo.append(c)
print(fibo[:N])
OUTPUT
N = 10
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
I can see few issues here:
fibo = [0,1]
# Your loop should start at index 2, otherwise
# you will try to access fibo[-2] and fibo[-1] with k=0
# and fibo[-1] and fibo[0] with k=1
# Also, if you only want 10 values, you should change 11
# to 10 since fibo already has two values.
# for k in range(11):
for k in range(2, 10):
# You don't want the content of fibo, but the indexes!
# i = fibo[-1]
# j = fibo[-2]
i = k - 1
j = k - 2
# You are already using k, so you need to use an other variable
# k = fibo[i] + fibo[j]
v = fibo[i] + fibo[j]
fibo.append(v)
# You don't need k+=1 because it will be incremented
# according to range(2, 10). The loop will start with
# k = 2 and it will stop when k = 9
# k += 1
print(fibo)
Your code did not crash because you technically can access fibo[-1] and fibo[-2]. It will respectively return the last value of your array, and the value before the last one.

String concatenation queries

I have a list of characters, say x in number, denoted by b[1], b[2], b[3] ... b[x]. After x,
b[x+1] is the concatenation of b[1],b[2].... b[x] in that order. Similarly,
b[x+2] is the concatenation of b[2],b[3]....b[x],b[x+1].
So, basically, b[n] will be concatenation of last x terms of b[i], taken left from right.
Given parameters as p and q as queries, how can I find out which character among b[1], b[2], b[3]..... b[x] does the qth character of b[p] corresponds to?
Note: x and b[1], b[2], b[3]..... b[x] is fixed for all queries.
I tried brute-forcing but the string length increases exponentially for large x.(x<=100).
Example:
When x=3,
b[] = a, b, c, a b c, b c abc, c abc bcabc, abc bcabc cabcbcabc, //....
//Spaces for clarity, only commas separate array elements
So for a query where p=7, q=5, answer returned would be 3(corresponding to character 'c').
I am just having difficulty figuring out the maths behind it. Language is no issue
I wrote this answer as I figured it out, so please bear with me.
As you mentioned, it is much easier to find out where the character at b[p][q] comes from among the original x characters than to generate b[p] for large p. To do so, we will use a loop to find where the current b[p][q] came from, thereby reducing p until it is between 1 and x, and q until it is 1.
Let's look at an example for x=3 to see if we can get a formula:
p N(p) b[p]
- ---- ----
1 1 a
2 1 b
3 1 c
4 3 a b c
5 5 b c abc
6 9 c abc bcabc
7 17 abc bcabc cabcbcabc
8 31 bcabc cabcbcabc abcbcabccabcbcabc
9 57 cabcbcabc abcbcabccabcbcabc bcabccabcbcabcabcbcabccabcbcabc
The sequence is clear: N(p) = N(p-1) + N(p-2) + N(p-3), where N(p) is the number of characters in the pth element of b. Given p and x, you can just brute-force compute all the N for the range [1, p]. This will allow you to figure out which prior element of b b[p][q] came from.
To illustrate, say x=3, p=9 and q=45.
The chart above gives N(6)=9, N(7)=17 and N(8)=31. Since 45>9+17, you know that b[9][45] comes from b[8][45-(9+17)] = b[8][19].
Continuing iteratively/recursively, 19>9+5, so b[8][19] = b[7][19-(9+5)] = b[7][5].
Now 5>N(4) but 5<N(4)+N(5), so b[7][5] = b[5][5-3] = b[5][2].
b[5][2] = b[3][2-1] = b[3][1]
Since 3 <= x, we have our termination condition, and b[9][45] is c from b[3].
Something like this can very easily be computed either recursively or iteratively given starting p, q, x and b up to x. My method requires p array elements to compute N(p) for the entire sequence. This can be allocated in an array or on the stack if working recursively.
Here is a reference implementation in vanilla Python (no external imports, although numpy would probably help streamline this):
def so38509640(b, p, q):
"""
p, q are integers. b is a char sequence of length x.
list, string, or tuple are all valid choices for b.
"""
x = len(b)
# Trivial case
if p <= x:
if q != 1:
raise ValueError('q={} out of bounds for p={}'.format(q, p))
return p, b[p - 1]
# Construct list of counts
N = [1] * p
for i in range(x, p):
N[i] = sum(N[i - x:i])
print('N =', N)
# Error check
if q > N[-1]:
raise ValueError('q={} out of bounds for p={}'.format(q, p))
print('b[{}][{}]'.format(p, q), end='')
# Reduce p, q until it is p < x
while p > x:
# Find which previous element character q comes from
offset = 0
for i in range(p - x - 1, p):
if i == p - 1:
raise ValueError('q={} out of bounds for p={}'.format(q, p))
if offset + N[i] >= q:
q -= offset
p = i + 1
print(' = b[{}][{}]'.format(p, q), end='')
break
offset += N[i]
print()
return p, b[p - 1]
Calling so38509640('abc', 9, 45) produces
N = [1, 1, 1, 3, 5, 9, 17, 31, 57]
b[9][45] = b[8][19] = b[7][5] = b[5][2] = b[3][1]
(3, 'c') # <-- Final answer
Similarly, for the example in the question, so38509640('abc', 7, 5) produces the expected result:
N = [1, 1, 1, 3, 5, 9, 17]
b[7][5] = b[5][2] = b[3][1]
(3, 'c') # <-- Final answer
Sorry I couldn't come up with a better function name :) This is simple enough code that it should work equally well in Py2 and 3, despite differences in the range function/class.
I would be very curious to see if there is a non-iterative solution for this problem. Perhaps there is a way of doing this using modular arithmetic or something...

Resources