Multiple Discrete1D distributions #866
-
Hello, Is there a way to define a data structure for multiple 1D discrete distirbutions and sample from them without looping through the distributions? Essentially, that means in the wavefront mode, per ray, we have a 1D distribution with a fixed number of elements that we want to sample from, again per ray. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 4 replies
-
Hi @saeedhd96, I think that should be possible. Here is a little reproducer. It only supports two distributions at the moment but if you can stack your distribution it should work as well. if __name__ == "__main__":
cdf1 = mi.Float(0.1, 0.3, 0.4, 0.6, 0.8, 1.0)
cdf2 = mi.Float(0.1, 0.2, 0.6, 0.7, 0.9, 1.0)
sample = mi.Float(0.6, 0.6)
cdf = dr.zeros(mi.Float, len(cdf1) + len(cdf2))
dr.scatter(cdf, cdf1, dr.arange(mi.UInt, len(cdf1)))
dr.scatter(cdf, cdf2, dr.arange(mi.UInt, len(cdf2)) + len(cdf1))
start = mi.UInt(0, len(cdf1))
idx = dr.binary_search(
0, len(cdf1), lambda idx: dr.gather(mi.Float, cdf, idx + start) < sample
)
print(f"{idx=}") Another option would be to implement your own binary search, where you can specify the start and end as Dr.Jit arrays. import mitsuba as mi
import drjit as dr
if __name__ == "__main__":
mi.set_variant("llvm_ad_rgb")
def binary_search(start: mi.UInt, end: mi.UInt, pred):
iterations = dr.select(start < end, dr.log2i(end - start) + 1, 0)
index = dr.zeros(mi.UInt, dr.width(pred(start)))
start_ = mi.UInt(start)
loop = mi.Loop("Binary Search", lambda: (start_, end, index))
while loop(index < iterations):
middle = (start_ + end) >> 1
cond = dr.detach(pred(middle))
start_ = dr.select(cond, dr.minimum(middle + 1, end), start_)
end = dr.select(cond, end, middle)
index += 1
return start_ - start
if __name__ == "__main__":
cdf1 = mi.Float(0.1, 0.3, 0.4, 0.6, 1.0)
cdf2 = mi.Float(0.1, 0.2, 0.6, 0.7, 0.9, 1.0)
sample = mi.Float(0.6, 0.6)
cdf = dr.zeros(mi.Float, len(cdf1) + len(cdf2))
dr.scatter(cdf, cdf1, dr.arange(mi.UInt, len(cdf1)))
dr.scatter(cdf, cdf2, dr.arange(mi.UInt, len(cdf2)) + len(cdf1))
start = mi.UInt(0, len(cdf1))
end = mi.UInt(len(cdf1), len(cdf1) + len(cdf2))
idx = binary_search(start, end, lambda idx: dr.gather(mi.Float, cdf, idx) < sample)
print(f"{idx=}") Hope this could help. |
Beta Was this translation helpful? Give feedback.
-
Thanks for your response.
import mitsuba as mi
import drjit as dr
mi.set_variant("cuda_ad_rgb")
size = 5
pdf = mi.Float([0.1, 0.2, 0.3, 0.4, 0.5, 0.1, 0.2, 0.3, 0.4, 0.5])
iterations = dr.width(pdf)
index = dr.zeros(mi.UInt32, iterations)
size = mi.UInt32(size)
cdf = dr.zeros(mi.Float, dr.width(pdf))
loop = mi.Loop("Cumsum", lambda: (index, cdf))
while loop(index<iterations):
# d = data[index]
d = dr.gather(mi.Float, pdf, index)
prev_sum = dr.gather(mi.Float, cdf, index-1)
a = dr.select(index%size!=0,prev_sum, 0)
dr.scatter(cdf, d+prev_sum, index)
index+=1 This raises an error
|
Beta Was this translation helpful? Give feedback.
Hi @saeedhd96,
I think that should be possible. Here is a little reproducer. It only supports two distributions at the moment but if you can stack your distribution it should work as well.