The closest implementation I can find is that of aiostream's chunks. Which allows the generation of "chunks of size n from an asynchronous sequence. The chunks are lists, and the last chunk might contain less than n elements".
I have implemented something similar but the key difference is that it prioritises fulfilling one batch at a time as opposed to multiple batches at once.
import asyncio
import aiostream
from collections import deque
class IterableAsyncQueue:
def __init__(self):
self.queue = asyncio.Queue()
async def put(self, value):
await self.queue.put(value)
def __aiter__(self):
return self
async def __anext__(self):
return await self.queue.get()
class Batch:
def __init__(self, n):
self.batch_size = n
def __call__(self, iterable, *args):
self.iterable = iterable
self.calls = deque()
self.pending = set()
self.pending_return = asyncio.Queue()
self.initialised = False
return self
def __iter__(self):
iterable = iter(self.iterable)
return iter(lambda: tuple(itertools.islice(iterable, self.batch_size)), ())
def __aiter__(self):
return self
async def __anext__(self):
self.pending |= {asyncio.create_task(self.iterable.__anext__()) for _ in range(self.batch_size)}
if self.initialised:
future = asyncio.get_running_loop().create_future()
self.calls.append(future)
await future
else:
self.initialised = True
batch = []
while len(batch) < self.batch_size:
done, _ = await asyncio.wait(self.pending, return_when=asyncio.FIRST_COMPLETED)
done = list(done)[0]
batch.append(await done)
self.pending.discard(done)
next_call = self.calls.popleft()
next_call.set_result(None)
return batch
async def consumer(n, a):
start = time.time()
async for x in a:
print(n, x, time.time() - start)
async def producer(q):
for x in range(50):
await asyncio.sleep(0.5)
await q.put(x)
q = IterableAsyncQueue()
# a = Batch(5)(q)
a = aiostream.stream.chunks(q, 5)
loop = asyncio.get_event_loop()
loop.create_task(producer(q))
loop.create_task(consumer(1, a))
loop.create_task(consumer(2, a))
loop.run_forever()
The output using aiostream.stream.chunks:
1 [0, 2, 4, 6, 8] 4.542179107666016
2 [1, 3, 5, 7, 9] 5.04422402381897
1 [10, 12, 14, 16, 18] 9.575451850891113
2 [11, 13, 15, 17, 19] 10.077155828475952
The output using my implementation of priority batch:
1 [0, 1, 2, 3, 4] 2.519313097000122
2 [5, 6, 7, 8, 9] 5.031418323516846
1 [10, 11, 12, 13, 14] 7.543889045715332
2 [15, 16, 17, 18, 19] 10.052537202835083
It seems to me that the priority batch is fundamentally more useful as it yields results sooner than chunks allowing the calling code to await another batch. This means that if there are m consumers each awaiting a batch of size n then there are always between m×n and (m-1)×n results being waited upon. With the chunks implementation the number of results being waited upon varies between m and m×n.
What I would like to know is why haven't I been able to find an implementation of this before and is this the best way of implementing this solution?
Related
My actual case is more complicated, but the MVCE is
from typing import List
def find_largest(numbers: List[int]) -> List[int]:
"""
>>> find_largest([3, 4, 5, 5, 3, 1, -2, 4, 3, 3])
[5, 5]
"""
assert len(numbers) > 0 # guaranteed by caller
largest_numbers = None
value = None
for number in numbers:
if value is None or number > value:
largest_numbers = [number]
value = number
elif number == value:
largest_numbers.append(number)
return largest_numbers
if __name__ == '__main__':
import doctest
doctest.testmod()
When I run mypy on this, I get:
mytest.py:18: error: Incompatible return value type (got "Optional[List[int]]", expected "List[int]")
Found 1 error in 1 file (checked 1 source file)
But restrictions which are not captured by mypy guarantee that None is not returned. How can I hint that to mypy? (Initializing with something else is NOT possible)
Your code can still return None according to Mypy and the it thinks the typing is correct.
Assuming you can't fix this you could also force the return to always have a value with:
assert largest_numbers
return largest_numbers
Alternatively, use typing.cast:
To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything (we want this to be as fast as possible).
from typing import List, cast
...
def find_largest(numbers: List[int]) -> List[int]:
"""
>>> find_largest([3, 4, 5, 5, 3, 1, -2, 4, 3, 3])
[5, 5]
"""
assert len(numbers) > 0 # guaranteed by caller
largest_numbers = None
value = None
for number in numbers:
if value is None or number > value:
largest_numbers = [number]
value = number
elif number == value:
largest_numbers.append(number)
return cast(List[int], largest_numbers)
...
If I have 2 lists:
list1 = [1, 2, 3, 4];
list2 = [10, 25, 35, 58];
and I want to get a list which has products of corresponding elements of 2 lists;
In Python one can do:
outlist = list(map(lambda a,b: a*b, list1, list2))
However, in D, I know of following method:
import std.stdio;
void main(){
auto list1 = [1, 2, 3, 4];
auto list2 = [10, 25, 35, 58];
int[] outlist;
foreach(i, item; list1){
outlist ~= item*list2[i];
}
writeln(outlist);
}
My questions are:
Q1: Can one keep both lists as argument of foreach?
Q2: How to multiply corresponding elements of 2 lists using map function?
Thanks for your insight.
The key is to use zip to combine the elements of the two lists (or dynamic arrays as they are known in D) to a single array of tuples before applying map or foreach. The tuple elements can be accessed with a zero based index (i.e. a[0] and a[1] in this example).
import std.algorithm.iteration : map;
import std.range : zip;
import std.stdio : writeln;
void main() {
auto list1 = [1, 2, 3, 4];
auto list2 = [10, 25, 35, 58];
// Question #2
auto list3 = zip(list1, list2).map!(a => a[0] * a[1]);
writeln(list3);
// Question #1
typeof(list1) list4;
foreach(a; zip(list1, list2)) {
list4 ~= a[0] * a[1];
}
writeln(list4);
}
The code above prints twice:
[10, 50, 105, 232]
as expected.
I am using three-dimensional convolution links (with ConvolutionND) in my chain.
The forward computation run smoothly (I checked intermediate result shapes to be sure I understood correctly the meaning of the parameters of convolution_nd), but during the backward a CuDNNError is raised with the message CUDNN_STATUS_NOT_SUPPORTED.
The cover_all parameter of ConvolutionND as its default value of False, so from the doc I don't see what can be the cause of the error.
Here is how I defind one of the convolution layers :
self.conv1 = chainer.links.ConvolutionND(3, 1, 4, (3, 3, 3)).to_gpu(self.GPU_1_ID)
And the call stack is
File "chainer/function_node.py", line 548, in backward_accumulate
gxs = self.backward(target_input_indexes, grad_outputs)
File "chainer/functions/connection/convolution_nd.py", line 118, in backward
gy, W, stride=self.stride, pad=self.pad, outsize=x_shape)
File "chainer/functions/connection/deconvolution_nd.py", line 310, in deconvolution_nd
y, = func.apply(args)
File chainer/function_node.py", line 258, in apply
outputs = self.forward(in_data)
File "chainer/functions/connection/deconvolution_nd.py", line 128, in forward
return self._forward_cudnn(x, W, b)
File "chainer/functions/connection/deconvolution_nd.py", line 105, in _forward_cudnn
tensor_core=tensor_core)
File "cupy/cudnn.pyx", line 881, in cupy.cudnn.convolution_backward_data
File "cupy/cuda/cudnn.pyx", line 975, in cupy.cuda.cudnn.convolutionBackwardData_v3
File "cupy/cuda/cudnn.pyx", line 461, in cupy.cuda.cudnn.check_status
cupy.cuda.cudnn.CuDNNError: CUDNN_STATUS_NOT_SUPPORTED
So are there special points to take care of when using ConvolutionND ?
A failing code is for instance :
import chainer
from chainer import functions as F
from chainer import links as L
from chainer.backends import cuda
import numpy as np
import cupy as cp
chainer.global_config.cudnn_deterministic = False
NB_MASKS = 60
NB_FCN = 3
NB_CLASS = 17
class MFEChain(chainer.Chain):
"""docstring for Wavelphasenet."""
def __init__(self,
FCN_Dim,
gpu_ids=None):
super(MFEChain, self).__init__()
self.GPU_0_ID, self.GPU_1_ID = (0, 1) if gpu_ids is None else gpu_ids
with self.init_scope():
self.conv1 = chainer.links.ConvolutionND(3, 1, 4, (3, 3, 3)).to_gpu(
self.GPU_1_ID
)
def __call__(self, inputs):
### Pad input ###
processed_sequences = []
for convolved in inputs:
## Transform to sequences)
copy = convolved if self.GPU_0_ID == self.GPU_1_ID else F.copy(convolved, self.GPU_1_ID)
processed_sequences.append(copy)
reprocessed_sequences = []
with cuda.get_device(self.GPU_1_ID):
for convolved in processed_sequences:
convolved = F.expand_dims(convolved, 0)
convolved = F.expand_dims(convolved, 0)
convolved = self.conv1(convolved)
reprocessed_sequences.append(convolved)
states = F.vstack(reprocessed_sequences)
logits = states
ret_logits = logits if self.GPU_0_ID == self.GPU_1_ID else F.copy(logits, self.GPU_0_ID)
return ret_logits
def mfe_test():
mfe = MFEChain(150)
inputs = list(
chainer.Variable(
cp.random.randn(
NB_MASKS,
11,
in_len,
dtype=cp.float32
)
) for in_len in [53248]
)
val = mfe(inputs)
grad = cp.ones(val.shape, dtype=cp.float32)
val.grad = grad
val.backward()
for i in inputs:
print(i.grad)
if __name__ == "__main__":
mfe_test()
cupy.cuda.cudnn.convolutionBackwardData_v3 is incompatible with some specific parameters, as described in an issue in official github.
Unfortunately, the issue only dealt with deconvolution_2d.py (not deconvolution_nd.py), therefore the decision-making about whether cudnn is used or not failed in your case, I guess.
you can check your parameter by confirming
check whether dilation parameter (!=1) or group parameter (!=1) is passed to the convolution.
print chainer.config.cudnn_deterministic, configuration.config.autotune, and configuration.config.use_cudnn_tensor_core.
Further support may be obtained by raising an issue in the official github.
The code you showed is much complicated.
To clarify the problem, the code below would help.
from chainer import Variable, Chain
from chainer import links as L
from chainer import functions as F
import numpy as np
from six import print_
batch_size = 1
in_channel = 1
out_channel = 1
class MyLink(Chain):
def __init__(self):
super(MyLink, self).__init__()
with self.init_scope():
self.conv = L.ConvolutionND(3, 1, 1, (3, 3, 3), nobias=True, initialW=np.ones((in_channel, out_channel, 3, 3, 3)))
def __call__(self, x):
return F.sum(self.conv(x))
if __name__ == "__main__":
my_link = MyLink()
my_link.to_gpu(0)
batch = Variable(np.ones((batch_size, in_channel, 3, 3, 3)))
batch.to_gpu(0)
loss = my_link(batch)
loss.backward()
print_(batch.grad)
My coin change dynamic programming implementation is failing for some of the test cases, and I am having a hard time figuring out why:
Problem Statement: Given an amount and a list of coins, find the minimum number of coins required to make that amount.
Ex:
Target Amount: 63
Coin List: [1, 5, 10, 21, 25]
Output: [21, 21, 21]
def coin_change(change_list, amount, tried):
if amount <= 0:
return []
if amount in change_list:
return [amount]
if amount in tried:
return tried[amount]
coin_count = []
for change in change_list:
if change < amount:
changes = coin_change(change_list, amount-change, tried)
changes.append(change)
coin_count.append(changes)
min_changes = coin_count[0][:]
for x in coin_count[1:]:
if len(min_changes) >= len(x):
min_changes = x[:]
tried[amount] = min_changes[:]
return min_changes
def main():
for amount in range(64):
changes = coin_change([1, 5, 10, 21, 25], amount, {})
if sum(changes) != amount:
print "WRONG: Change for %d is: %r" % (amount, changes)
else:
# print "Change for %d is: %r" % (amount, changes)
pass
if __name__ == "__main__":
main()
Trinket: https://trinket.io/python/43fcff035e
You're corrupting the variable, changes, by appending to it during a loop. Try this:
Replace these two lines:
changes.append(change)
coin_count.append(changes)
With:
_changes = changes[:] + [change]
coin_count.append(_changes)
What is the most concise way of converting a java.util.List into a normal
JavaFX sequence (in JavaFX)?
e.g.
def myList = java.util.Arrays.asList(1, 2, 3);
def mySequence = ... // a sequence containing [1, 2, 3]
This is the most concise way I could find - there may be a more direct method though
def myList = java.util.Arrays.asList(1, 2, 3);
def mySequence = for (i in myList) i;
println("mySequence={mySequence}");