witwin.channel.montecarlo.bdpt

API reference for the bidirectional path tracing (BDPT) solver - Config, solve, Result and exported BDPTPathSamples.

The BDPT solver traces subpaths from the transmitters and from the receivers and connects them, weighting each connection with multiple importance sampling (MIS). It finds paths that are hard to hit from the transmitter side alone, at a higher cost per sample than Monte Carlo Basic.

from witwin.channel.montecarlo.bdpt import BDPTPathSamples, Config, Result, solve

Config#

Config(
    samples=4096,
    seed=0,
    max_depth=3,
    max_light_depth=None,
    max_diffraction_order=1,
    max_scattering_order=1,
    coupled_paths=False,
    coupled_candidate_limit=1_000_000,
    components=frozenset({"los", "reflection", "diffraction"}),
    coherent=False,
    mis="power_heuristic",
    power_heuristic_beta=2.0,
    receiver_strategy="grid_area",
    accumulation_strategy="auto",
    sample_streams=1,
    diagnostics=False,
    export_paths=False,
    max_exported_paths=None,
    ad_mode="none",
    workspace_limit_bytes=1 << 30,
)
samplesintdefault 4096

Number of samples per transmitter, positive.

seedintdefault 0

Random seed, at least 0.

max_depthintdefault 3

Maximum interactions of a complete connected path. Note the default is 3, not 1 as in the other solvers.

max_light_depthint | Nonedefault None

Maximum depth of the transmitter-side subpath, at least 0. None uses max_depth.

max_diffraction_orderintdefault 1

0 or 1. With "diffraction" in components it must be 1.

max_scattering_orderintdefault 1

Maximum diffuse-scattering events per path, at least 1.

coupled_pathsbooldefault False

Trace reflection-diffraction coupled paths. Requires max_depth >= 2 with "reflection" and "diffraction".

coupled_candidate_limitintdefault 1000000

Coupled-candidate bound, 1 to 1,000,000.

componentsset[str]default frozenset({'los', 'reflection', 'diffraction'})

Non-empty subset of "los", "reflection", "diffraction", "transmission", "scattering". Scattering requires max_depth >= 1.

coherentbooldefault False

False adds powers. True adds line-of-sight, reflection and diffraction contributions as complex phasors per transmitter, receiver and component. Not combinable with "transmission" or "scattering".

misstrdefault 'power_heuristic'

MIS weighting: "power_heuristic", "balance" or "none".

power_heuristic_betafloatdefault 2.0

Exponent of the power heuristic, positive.

receiver_strategystrdefault 'grid_area'

Receiver sampling strategy: "grid_area" or "point_sphere".

accumulation_strategystrdefault 'auto'

How contributions are summed on the GPU: "auto", "atomic", "staged" or "compact". "auto" picks one.

sample_streamsintdefault 1

Number of independent random streams, positive.

diagnosticsbooldefault False

True fills Result.diagnostics and also publishes Result.variance.

export_pathsbooldefault False

True fills Result.path_samples.

max_exported_pathsint | Nonedefault None

Limit on exported sample rows, at least 0.

ad_modestrdefault 'none'

"none", "jvp" or "vjp". Gradients are experimental; see Differentiability.

workspace_limit_bytesint | Nonedefault 1073741824

GPU workspace budget in bytes (1 GiB), or None. Exceeding it raises.

solve#

solve(scene, config, *, reference_frequency_hz) -> Result
scenewitwin.core.Scene | witwin.core.SceneSnapshotrequired

The world. It may contain point receivers or one ReceiverGrid, not both.

configmontecarlo.bdpt.Configrequired

This module's Config.

reference_frequency_hzfloat | torch.Tensorrequired

Carrier frequency in hertz.

Returns a Result. Raises on an invalid configuration, an unsupported capability, workspace overflow or a native failure.

from witwin.channel.montecarlo import bdpt
 
result = bdpt.solve(
    scene,
    bdpt.Config(samples=65_536, max_depth=1, components={"los", "reflection"}, seed=1, diagnostics=True),
    reference_frequency_hz=3.0e9,
)
print(result.path_gain.shape, result.variance.shape)

Result#

FieldType and shapeMeaning
path_gainfloat32; (T, R) for point receivers, (T, columns, rows) for a ReceiverGridEstimated received power, including power_w.
component_powerdict[str, Tensor]Total power per component. "transmission" and "scattering" appear only when requested.
component_mapsdict[str, Tensor] or NonePer-component (T, columns, rows) maps for a grid.
varianceTensor or NoneEstimator variance, shaped like path_gain, when diagnostics=True.
path_samplesBDPTPathSamples or NoneExported samples when export_paths=True.
metadatadictSamples, seed, MIS, accumulation, build and execution records.
diagnosticsdict or NoneShapes and counters when diagnostics=True.

BDPTPathSamples#

Exactly N exported sample rows.

FieldShapeMeaning
topology(N, 4)Encoded discrete path topology.
contribution(N,)Sample contribution.
pdf(N,)Sampling probability density.
mis_weight(N,)MIS weight.
component_id(N,)Component code.
valid(N,)Row validity.
tx_id, rx_id(N,)Transmitter and receiver of the sample.
grid_linear_id(N,)Linear grid-sample index, or a sentinel for point receivers.
light_depth, sensor_depth(N,)Depth of the transmitter-side and receiver-side subpaths.
path_length_m(N,)Total path length in metres.

The width of topology was 4 in a max_depth=1 solve; it can differ for other configurations.