Add generate to MLP and Transformer WaveFunction classes - #201
Conversation
Co-authored-by: hzhangxyz <11623447+hzhangxyz@users.noreply.github.com>
generate to MLP and Transformer WaveFunction classes
There was a problem hiding this comment.
Pull request overview
Implements batched autoregressive sampling in generate() for MLP and Transformer wavefunction networks, replacing previous NotImplementedError stubs and updating module docstrings accordingly.
Changes:
- Added autoregressive batched sampling + deduplication + amplitude evaluation to 6
generate()methods. - Updated return signature to
(configs, amplitudes, counts, None)with multiplicity as the 3rd element. - Simplified file-level docstrings to remove references to the prior sampling-method paper.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| qmp/networks/transformers.py | Implements generate() for 3 transformer wavefunctions (with KV-cache) and updates docstring. |
| qmp/networks/mlp.py | Implements generate() for 3 MLP wavefunctions and updates docstring. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| real_batch_size: int = len(x_unique) | ||
| real_batch_size_block: int = real_batch_size // block_num | ||
| remainder: int = real_batch_size % block_num | ||
| amplitude_list: list[torch.Tensor] = [] | ||
| for j in range(block_num): | ||
| if j < remainder: | ||
| current_real_batch_size_block: int = real_batch_size_block + 1 | ||
| else: | ||
| current_real_batch_size_block = real_batch_size_block | ||
| start_index: int = j * real_batch_size_block + min(j, remainder) | ||
| end_index: int = start_index + current_real_batch_size_block | ||
| batch_indices_block: torch.Tensor = torch.arange(start_index, end_index, device=device, dtype=torch.int64) | ||
| amplitude_block: torch.Tensor = self(x_unique[batch_indices_block]) |
There was a problem hiding this comment.
block_num is used as a divisor and loop bound without validation. block_num == 0 will raise a ZeroDivisionError, and block_num > real_batch_size can produce empty blocks (which may break forward() if it can’t handle empty batches). Consider clamping/validating block_num (e.g., enforce block_num >= 1 and block_num <= real_batch_size when real_batch_size > 0) and/or skipping blocks where current_real_batch_size_block == 0.
| tuple[torch.Tensor, torch.Tensor, torch.Tensor, None] | ||
| A tuple containing the generated configurations, their amplitudes, their sample counts, and a None value. | ||
| The configurations are a two-dimensional uint8 tensor with first dimension equal to the number of unique configurations. | ||
| The second dimension contains occupation for each qubit which is bitwise encoded. |
There was a problem hiding this comment.
For WaveFunctionNormal (general physical_dim), describing the packed representation as “occupation for each qubit” is misleading; these are packed bytes encoding per-site local states (possibly using multiple bits per site). Consider updating this line to describe the packed layout generically (e.g., “packed-site encoding in uint8”) and avoid implying a qubit-only interpretation.
| The second dimension contains occupation for each qubit which is bitwise encoded. | |
| The second dimension encodes the local state for each site using a packed uint8 representation (bitwise encoding, potentially using multiple bits per site). |
| real_batch_size_block: int = real_batch_size // block_num | ||
| remainder: int = real_batch_size % block_num | ||
| amplitude_list: list[torch.Tensor] = [] | ||
| for j in range(block_num): | ||
| if j < remainder: | ||
| current_real_batch_size_block: int = real_batch_size_block + 1 | ||
| else: | ||
| current_real_batch_size_block = real_batch_size_block | ||
| start_index: int = j * real_batch_size_block + min(j, remainder) | ||
| end_index: int = start_index + current_real_batch_size_block | ||
| batch_indices_block: torch.Tensor = torch.arange(start_index, end_index, device=device, dtype=torch.int64) | ||
| amplitude_block: torch.Tensor = self(x_unique[batch_indices_block]) | ||
| amplitude_list.append(amplitude_block) | ||
| amplitude: torch.Tensor = torch.cat(amplitude_list) | ||
|
|
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| x: torch.Tensor = pack_int(samples_ordered.view([batch_size, self.double_sites]), size=1) | ||
|
|
||
| # Deduplicate and count. | ||
| x_unique, _, counts = torch.unique(x, sorted=True, return_inverse=True, return_counts=True, dim=0) |
There was a problem hiding this comment.
torch.unique(..., sorted=True, dim=0) can add noticeable overhead for large batch_size due to sorting. If callers don’t rely on x_unique being sorted, consider using sorted=False for better performance (keeping return_counts=True for multiplicities).
| x_unique, _, counts = torch.unique(x, sorted=True, return_inverse=True, return_counts=True, dim=0) | |
| x_unique, _, counts = torch.unique(x, sorted=False, return_inverse=True, return_counts=True, dim=0) |
Six
generatestubs that raisedNotImplementedErrornow implement batched autoregressive sampling per arXiv:2109.12606 Algorithm 1. Return signature is(configs, amplitudes, multiplicity, None)— multiplicity is 3rd (not 4th), noreal_prob,Nonereserved for future use.Affected classes
mlp.WaveFunctionElectronUpDown— 4 states/site, spin-conservation maskmlp.WaveFunctionElectron— 2 states/site, particle-number maskmlp.WaveFunctionNormal— 2 states/site, no conservationtransformers.WaveFunctionElectronUpDown— same as MLP variant, uses KV-cachetransformers.WaveFunctionElectron— same as MLP variant, uses KV-cachetransformers.WaveFunctionNormal— generalphysical_dim, no mask, uses KV-cacheAlgorithm
Each implementation samples
batch_sizeconfigurations autoregressively viatorch.multinomial, then deduplicates withtorch.uniqueto produce unique configs + their multiplicities. Amplitudes are computed by callingforward()on the unique set. All six are@torch.jit.export-compatible.Transformer variants reuse
_blocked_forward_for_generate_uniquewith KV-cache for efficient single-token-at-a-time inference;block_numis honored for both the sampling loop and the final amplitude pass.Docstrings
Removed "sampling method introduced in https://arxiv.org/pdf/2408.07625" from both file-level docstrings —
mlp.pynow says "MLP network from https://arxiv.org/pdf/2109.12606",transformers.pysays "auto-regressive transformers network".Original prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.