I would like to make all the values in the first list inside the list of lists named "child_Before" below zero. The piece of code I wrote to accomplish this task is also shown below after the list:
child_Before = [[9, 12, 7, 3, 13, 14, 10, 5, 4, 11, 8, 6, 2],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1],
[[1, 0], [1, 1]]]
for elem in range(len(child_Before[0])):
child_Before[0][elem] = 0
Below is the expected result:
child_After = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1],
[[1, 0], [1, 1]]]
However, I think there should be a more nibble way to accomplish this exercise. Hence, I welcome your help. Thank you in advance.
just to add a creative answer
import numpy as np
child_Before[0] = (np.array(child_Before[0])&0).tolist()
this is bad practice though since i'm using bitwise operasions in a senario where it is not intuitive, and i think there is a slight chance i'm making 2 loops xD on the bright site the & which is making all the zeros is O(1) time complexity
Just create a list of [0] with the same length as the original list.
# Answer to this question - make first list in the list to be all 0
child_Before[0] = [0] * len(child_Before[0])
As for you answer, I can correct it to make all the elements in the lists of this list to be zero.
# Make all elements 0
for child in range(len(child_Before)):
child_Before[child] = [0] * len(child_Before[child])
Use list comprehension:
child_after = [[i if n != 0 else 0 for i in j] for n, j in enumerate(child_Before)]
I am trying to distribute a 2D array using Scatterv, which works fine. However, the corresponding Gatherv operation gives an error: message truncated. Can someone explain what I am doing wrong.
program scatterv
use mpi
implicit none
integer, allocatable, dimension(:,:) :: array
integer, allocatable, dimension(:) :: chunk
integer, allocatable, dimension(:) :: displacement
integer, allocatable, dimension(:) :: sendcounts
integer :: mpi_ierr, mpi_rank, mpi_size
integer, parameter :: kWidth=4
call MPI_INIT(mpi_ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, mpi_rank, mpi_ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, mpi_size, mpi_ierr)
if (mpi_rank == 0) then
allocate(array(mpi_size, kWidth))
allocate(displacement(mpi_size))
allocate(sendcounts(mpi_size))
displacement = (/0, 0, 0, 0, 0, 0, 0, 0, 0, 0/)
sendcounts = (/2, 10, 5, 8, 5, 2, 2, 2, 2, 2/)
endif
allocate(chunk(mpi_size))
call MPI_SCATTERV(array, sendcounts, displacement, MPI_INTEGER, chunk, mpi_size, MPI_INTEGER, 0, MPI_COMM_WORLD, mpi_ierr)
...
call MPI_GATHERV(chunk, mpi_size, MPI_INTEGER, array, sendcounts, displacement, MPI_INTEGER, 0, MPI_COMM_WORLD, mpi_ierr)
if (mpi_rank == 0) then
deallocate(array)
deallocate(displacement)
end if
deallocate(chunk)
call MPI_FINALIZE(mpi_ierr)
end program scatterv
There are multiple errors in the code presented here.
1) All displacements are equal:
if (mpi_rank == 0) then
...
displacement = (/0, 0, 0, 0, 0, 0, 0, 0, 0, 0/)
sendcounts = (/2, 10, 5, 8, 5, 2, 2, 2, 2, 2/)
endif
The MPI standard mandates that no location in the send buffer should be read twice and no location in the receive buffer should be written twice. In order words, all chunks must be disjoint. Displacements are allowed to be equal only if the corresponding send counts are 0 (zero).
Some (if not most) MPI libraries do not enforce this condition for performance reasons. It might work, it might not work, all depending on the device used to transfer the data. Even if it works, it is still not correct MPI.
2) The receive count in MPI_SCATTERV does not match the chunk size:
call MPI_COMM_SIZE(MPI_COMM_WORLD, mpi_size, mpi_ierr)
...
sendcounts = (/2, 10, 5, 8, 5, 2, 2, 2, 2, 2/)
...
call MPI_SCATTERV(array, sendcounts, displacement, MPI_INTEGER, &
chunk, mpi_size, MPI_INTEGER, &
0, MPI_COMM_WORLD, mpi_ierr)
While for point-to-point operations one could provide a buffer that is larger than what the message actually occupies, with collective operations this is not the case - the amount of data sent to a process must match the size of the receive buffer as specified by the process. Some implementations are fine with with larger buffers but programs that rely on that are not correct.
The only reason the scatter operation works is that you have 10 MPI processes (judging from the size of the array initialiser) and the largest chunk size is also 10.
3) The same applies in reverse to the gather operation. But in that case all send counts except one (for rank 1) are bigger than the expected chunk size.
The corrected version of the program should look like this:
call MPI_INIT(mpi_ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, mpi_rank, mpi_ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, mpi_size, mpi_ierr)
allocate(sendcounts(mpi_size))
sendcounts = (/2, 10, 5, 8, 5, 2, 2, 2, 2, 2/)
if (mpi_rank == 0) then
allocate(array(mpi_size, kWidth))
allocate(displacement(mpi_size))
displacement = (/0, 2, 12, 17, 25, 27, 29, 31, 33, 35/)
endif
allocate(chunk(mpi_size))
call MPI_SCATTERV(array, sendcounts, displacement, MPI_INTEGER, &
chunk, sendcounts(mpi_rank+1), MPI_INTEGER, &
0, MPI_COMM_WORLD, mpi_ierr)
...
call MPI_GATHERV(chunk, sendcounts(mpi_rank+1), MPI_INTEGER, &
array, sendcounts, displacement, MPI_INTEGER, &
0, MPI_COMM_WORLD, mpi_ierr)
if (mpi_rank == 0) then
deallocate(array)
deallocate(displacement)
end if
deallocate(chunk)
deallocate(sendcounts)
call MPI_FINALIZE(mpi_ierr)
Note the use of +1 in sendcounts(mpi_rank+1). MPI ranks are numbered staring from 0 while Fortran array indexes start from 1 unless specified otherwise.
Also, you should not use the mpi_ prefix for naming your own subroutines/functions/modules/variables in order to prevent name clashes with true MPI symbols.
The problem is that amount of data sent is greater than the amount of data that the root told MPI it expects. You created an array called sendcounts that has some counts that the root process will use to assign spaces in the array to different ranks, however each process is sending mpi_size, which is probably bigger than some of the sendcounts (like 2 for instance). You need to make sure that the numbers match up. You can find an example code here.
I am trying to learn about arrays. I know that python has lists, not arrays, but the idea is the same. I have a list of lists setup like an array, and I am trying to modify them for random art fun, but I can only get one resulting random number out of this piece of code.
##prior list creation code making a large list of zeros called "array"###
while 3 not in array:
i= random.randint(1,9)
j= random.randint(1,28)
if (i%3)!=0 and (j%7)!=0:
if 5 in array:
array[i][j]=3
return array
elif 3 in array:
array[i][j]=4
return array
else:
array[i][j]=5
amy=(i, j)
return array
continue
##the resulting list called "array" does not chnange any zero to any number except one to "5"##
I have cut the code that made an array filled with zeros. The only number that will show up is the 5... ideally, I would have each number only show up once with each run, but in different spots
What am I doing wrong? I don't fully understand arrays, so that might be it, but I'm having trouble searching what I think the problem might be. Any help you can provide would be great!
Edit:
Sorry about forgetting, the array is the proper size to hold the data (9 rows by 28columns), and it isn't throwing any errors or exceptions... that should have been in there before I posted.
It's hard to tell exactly what you are asking, but I think you just wanted to randomly assign the numbers 3, 4, and 5 somewhere inside your matrix with the condition that i is not divisible by 3 and j is not divisible by 7. If that's what you want, then this should do it:
import random
array = [ [ 0 for j in range(28) ] for i in range(9) ]
for n in [3, 4, 5]:
while True:
i = random.randint(1, 9)
if i % 3 != 0:
break
while True:
j = random.randint(1, 28)
if j % 7 != 0:
break
array[i][j] = n
print('\n'.join(str(a) for a in array))
So let's talk about how to get to this answer. First off, we want a way to generate random numbers until a condition happens. In most languages, this would involve a do-while loop, but Python doesn't have those. However, we can make something that is equivalent to a do-while loop using just a while loop:
# This says to run this loop *forever*
while True:
# do something here
pass # This means "do nothing" in Python
if condition:
# This says if the previously mentioned condition
# is True then we will stop executing the
# currently containing loop.
break
So this construct, that I just showed is a building block which you can use to make a loop that runs until a condition is met.
Let's see how that fits in your original example. We want a random number in the range [1, 9) that is not divisible by 3. The random.randint function will provide a random number in that range, but it doesn't guarantee that it is not divisible by 3. So we need to enforce that constraint ourselves. One way to accomplish that is to simply generate a new number if the constraint is not met.
So now we can use the previously discussed loop construct to build a loop that runs until we have a number that is not divisible by 3:
while True:
i = random.randint(1, 9)
if i % 3 != 0:
break
I'm sure you can do the logical replacement in the previous loop to see how it fits the other example.
So now we can talk a little bit more about where you went wrong in your original code, and how you can prevent from making those same mistakes in the future.
Let's talk about this line first:
while 3 not in array:
First of all, when developing code, it's a very good idea to try things out at the Python interactive interpreter prompt. This is especially true when you are trying out a feature of the language for the first time. As I showed in my comment, the condition in your while loop is always False. The reason for that is clear if you try it out in the interpreter:
>>> 3 in [[3],[3],[3]]
False
The in operator only looks one level deep into a list. Also, it's always a good idea to start small when testing things out interactively. Notice I'm using a list of only 3 elements with nested lists containing only 1 element each instead of your original example of a 9 element list with nested lists containing 28 elements.
Now, another approach that we could have taken to make your loop condition change over time would be to make a "recursive" version of the in operator. Alternatively, we could have just hard coded it to expect a list that contains lists. I'm going to take this second approach because it is simpler, and I don't know if you are already familiar with recursion, and this is already a long explanation.
def contains2d(outer, element):
"""Expects a 2D-array-like list. Returns True if any of the inner
sub-lists within the outer list contain element.
"""
return any([e in inner for inner in outer])
If we try this function out, we'll see that it behaves as you originally expected the in operator to behave:
>>> contains2d([[3],[3],[3]], 3)
True
>>> contains2d([[0],[0],[3]], 3)
True
>>> contains2d([[0],[0],[0]], 3)
False
So now let's talk about your misunderstanding of the continue keyword. A while loop repeats automatically. You don't need to use continue inside of a while loop for it to repeat. The continue keyword is only used to skip the rest of the body of a loop. This is generally used if you have some special case for which you don't want to do the normal loop processing. Here's a reasonable example of how you might use continue:
>>> for i in range(10):
... if i % 3 == 0:
... continue
... print(i)
...
1
2
4
5
7
8
Notice that it really doesn't make any sense to put continue at the end of a loop:
while True:
print("This loop runs forever!")
# The following continue is useless
continue
Alright, so now that we have contains2d, we can start to think about what we want. In your example, you say you want the array variable to contain 3,4, and 5 at the end of your loop. Again, let's start small and see if the condition is True under the desired circumstances. We know from earlier that contains2d([[0],[0],[3]], 3) == True, so that is an insufficient loop termination criteria. Remember, we want the loop to only be true when all conditions have been met. So that means we need to use the and operator
>>> contains2d([[0],[0],[3]], 3) and contains2d([[0],[0],[3]], 4) and contains2d([[0],[0],[3]], 5)
False
>>> contains2d([[4],[0],[3]], 3) and contains2d([[4],[0],[3]], 4) and contains2d([[4],[0],[3]], 5)
False
>>> contains2d([[4],[5],[3]], 3) and contains2d([[4],[5],[3]], 4) and contains2d([[4],[5],[3]], 5)
True
Note that this condition is very ugly and hard to write. Ideally, we'd probably like to refactor it. One way to do that is to use the built-in all function. I'll let you experiment with it on your own, but here's the end result:
>>> all([contains2d([[4],[5],[3]], e) for e in [3,4,5]])
True
That's much shorter and much more clear. So, moving on, we want to run this loop as long as that condition is not True:
while not all([contains2d(array, e) for e in [3,4,5]]):
# ...
The next two lines are actually just fine, but I would format them according to PEP-8:
while not all([contains2d(array, e) for e in [3,4,5]]):
i = random.randint(1, 9)
j = random.randint(1, 28)
# ...
If we replace the in conditions with our contains2d function, then we're almost getting to a working solution:
while not all([contains2d(array, e) for e in [3,4,5]]):
i = random.randint(1, 9)
j = random.randint(1, 28)
if i % 3 != 0 and j % 7 != 0:
if contains2d(array, 5):
# ...
elif contains2d(array, 3):
# ...
else:
# ...
The assignments within the if conditions are perfectly fine as well. However, when you have a return inside of a loop, that will exit the loop and exit the entire containing function. In this code you don't actually have a containing function, so that will just end your program. That's not what you want to do here, so let's just drop all of those return statements:
while not all([contains2d(array, e) for e in [3,4,5]]):
i = random.randint(1, 9)
j = random.randint(1, 28)
if i % 3 != 0 and j % 7 != 0:
if contains2d(array, 5):
array[i][j] = 3
elif contains2d(array, 3):
array[i][j] = 4
else:
array[i][j] = 5
This program is very close to being right. However, the innermost if block is using some odd logic. If you run this progam using my initialization and output, you'll see something like this:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 5, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 3, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
It actually took me a minute to figure out what was going wrong, but the key obsevation is that the first time through the loop for which you get a successful pair of i and j, you'll take the else branch randomly placing a 5 in the nested list. The next successful time you get inside the if, you'll take the first branch which randomly places a 3 inside the nested list. However, here's where things go sideways. On the next succssful iteration, you'll still take the first branch, so you'll randomly place another 3 inside the nested list. You will keep doing this for several more iterations. Eventually, you will overwrite the previously written 5. At that point, the first condition will no longer hold and instead you will start taking the second branch which randomly places a 4 in the nested list. This branch will continue to be taken since 5 is no longer present in the nested list. Eventually, you will overwrite all of the previously written 3s with 4s in the nested list. At that point, the second branch condition will no longer hold, and you will land on the else branch again, writing a 5 to the nested list. Finally, the first condition will hold again, so on the next successful iteration, you'll write a 3 again, and as long as you don't get unlucky, that will miss the 5 and you'll now have met the termination criteria for the outer while loop leaving you with a bunch of 4s and just a single 3 and a single 5.
Maybe this is what you wanted to do, but it didn't seem that way from your question, so let's assume you really just wanted one of each of the numbers present in the nested list. If that's the case, we can easily correct the previous program. We just need to fix the conditions for each of the if cases. We only want to take the first branch when a 5 is present, but a 3 is not present. Thus that gives us if contains2d(array, 5) and not contains2d(array, 3):. Furthermore, we only want to take the second branch if 5 and 3 are both already present. Thus that gives us elif contains2d(array, 5) and contains2d(array, 3):. Finally, we only want to take the last branch if a 5 is not present. Thus we must change the else to another elif giving us elif not contains2d(array, 5):. Putting this all together gives us:
array = [ [ 0 for j in range(28) ] for i in range(9) ]
while not all([contains2d(array, e) for e in [3,4,5]]):
i = random.randint(1, 9)
j = random.randint(1, 28)
if i % 3 != 0 and j % 7 != 0:
if contains2d(array, 5) and not contains2d(array, 3):
array[i][j] = 3
elif contains2d(array, 5) and contains2d(array, 3):
array[i][j] = 4
elif not contains2d(array, 5):
array[i][j] = 5
print('\n'.join(str(a) for a in array))
This actually works like my original answer. However, it's not very satisfactory because the logic inside the if block is quite complicated. There's actually very rigid sequence of events that must happen. Whenever you think of a sequence of things, you should think of a list. In this case, the sequence goes like this, we assign 5, then we assign 3, then finally we assign 4. That can be represented as this list: [5, 3, 4]. If we reorder things a bit, we can get the following program:
array = [ [ 0 for j in range(28) ] for i in range(9) ]
for n in [5, 3, 4]:
while not contains2d(array, n):
i = random.randint(1, 9)
j = random.randint(1, 28)
if i % 3 != 0 and j % 7 != 0:
array[i][j] = n
print('\n'.join(str(a) for a in array))
This particular program does have a flaw in that it's possible for one of the later values to overwrite one of the earlier values, and thus the postcondition of all the numbers being present could possibly not hold if your random numbers happen to collide. In fact, my initial answer has this same issue. I'll leave fixing that as an exercise for you to figure out.