PML-282 Photonic Generator#220
Conversation
LF-Vigneux
left a comment
There was a problem hiding this comment.
The form and structure of the code seems fine to me. This is not a complete review pending the remaining functions to implement.
CassNot
left a comment
There was a problem hiding this comment.
There are some discrepancy with the orignal code
- (orignal perceval) https://github.com/Quandela/photonic-qgan
- (new MerLin) https://github.com/merlinquantum/reproduced_papers/tree/main/papers/photonic_QGAN
I am describing these discrepancies in review comments
More of a product question: What happens if the subgenerators are all the same ? Do we need to create multiple times the same one or should there be a shortcut to have to only define it once, then through the API, we precise the circuit/layer structure and N ones would be initiated ? (as in the original one)
Lack of validation: to validate this implementation, I would test it in the same settings as the original work (implementation here https://github.com/merlinquantum/reproduced_papers/tree/main/papers/photonic_QGAN) in a dedicated notebook
| raise NotImplementedError | ||
|
|
||
|
|
||
| class VectorAdapter(OutputAdapter): |
There was a problem hiding this comment.
Both classes VectorAdapter and ImageAdapter do not preserve the original QGAN mapping
Here: they flatten the outputs and crop/pad them directly, ignoring the output_keys
In the original work: the orignal PatchGenerator regroups Fock states through output_map and sums collision with index_add_ (filters lossy/non-PNR states + renormalizes) then maps to pixels -> see method dist_to_image in https://github.com/merlinquantum/reproduced_papers/blob/main/papers/photonic_QGAN/lib/generators.py
Therefore, in this implementation, the mapping depends on the MerLin's combinadics and not the paper mapping (for which the code is the same in the reproduced_papers repo as in the original work, in utils/mappings.py)
There was a problem hiding this comment.
will be changed in next commit
| return flattened | ||
|
|
||
|
|
||
| def _center_crop_or_pad(x: torch.Tensor, size: int) -> torch.Tensor: |
There was a problem hiding this comment.
Here, you apply cropping and padding after concatenating all heads
But the orignal work applied it per batch before concatenation (line 173 in https://github.com/merlinquantum/reproduced_papers/blob/main/papers/photonic_QGAN/lib/generators.py)
| If ``std`` is not positive. | ||
| """ | ||
|
|
||
| def __init__(self, dim: int, mean: float = 0.0, std: float = 1.0) -> None: |
There was a problem hiding this comment.
Default latent sampling does not match the reproduction training loop (line 142 https://github.com/merlinquantum/reproduced_papers/blob/main/papers/photonic_QGAN/lib/qgan.py)
PhotonicGenerator.generate() defaults to NormalLatent(..., std=1.0), while the original QGAN samples latent inputs with std 2 * pi
There was a problem hiding this comment.
A regression test should also compare a fixed set of output_keys and synthetic probability tensors against the original regroup/crop/normalize behavior
There was a problem hiding this comment.
correct, will do in next commit
|
@codex review with respect to original implementation https://github.com/merlinquantum/reproduced_papers/tree/main/papers/photonic_QGAN |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c529552be
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| flattened = _flatten_tensor_outputs(measurements.outputs) | ||
| combined = torch.cat(flattened, dim=1) | ||
| return _center_crop_or_pad(combined, self.size) |
There was a problem hiding this comment.
Reintroduce output-key remapping before image/vector adaptation
The new adapters flatten and crop raw tensor columns directly, but the original photonic_QGAN PatchGenerator path explicitly remaps probabilities with output_map (index_add_) before resizing each patch. In non-PNR/lossy settings, multiple Fock keys must be merged (and some keys filtered) to represent the same classical bin; skipping that remap changes the generated sample distribution rather than just reshaping it, so outputs are no longer faithful to the original implementation behavior.
Useful? React with 👍 / 👎.
| if latent is None: | ||
| latent = NormalLatent(inferred_dim) |
There was a problem hiding this comment.
Match default latent scale used by original photonic QGAN
When latent=None, the generator defaults to NormalLatent(..., std=1.0), but the original photonic_QGAN training code samples generator noise with torch.normal(0, 2 * torch.pi, ...). Any workflow using sample_latent()/generate() with defaults will therefore feed a much narrower latent distribution than the reference implementation, which can materially change training dynamics and reproduction results.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
correct, will be changed soon
CassNot
left a comment
There was a problem hiding this comment.
We still miss:
- a demo notebook illustrating that this model works as in https://github.com/merlinquantum/reproduced_papers/tree/main/papers/photonic_QGAN with Adam optimizer
- maybe you could add the original image from the paper in the documentation to illustrate how the model works (it is quite clear with it)
There was a problem hiding this comment.
This grouping strategy requires the keys and therefore breaks the nn.Sequential pipeline we can have with the other ones
Maybe you can move it to models/photonic_generator.py for now as we know it works for the GAN but we have not tested it on other models
This way, we can use it externally but it does not surprise users if it breaks nn.Sequential pipeline
There was a problem hiding this comment.
Pull request overview
This PR introduces a new PhotonicGenerator model under merlin.models that composes one or more QuantumLayer heads into a latent-to-sample generator, and adds an OccupancyGrouping policy used by photonic QGAN-style image pipelines. It also wires up the necessary plumbing in QuantumLayer to bind key-aware groupings to layer output keys.
Changes:
- Add
PhotonicGenerator,GeneratorMeasurements,LatentDistribution/NormalLatent, andOutputAdapter/VectorAdapter/ImageAdapterin a newmerlin/models/photonic_generator.py. - Add
OccupancyGroupinginmerlin/utils/grouping.pyplus_bind_grouping_to_output_keys, and integrate output-key binding intoQuantumLayerso probability groupings can refineoutput_keys/output_size. - Export new public APIs from
merlinandmerlin.models, and add Sphinx pages for the new grouping and generator modules.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| merlin/models/photonic_generator.py | New generator model, latent/output-adapter abstractions, and concrete vector/image adapters. |
| merlin/utils/grouping.py | New OccupancyGrouping plus shared key-binding helper. |
| merlin/utils/init.py | Exports OccupancyGrouping. |
| merlin/measurement/strategies.py | Accepts OccupancyGrouping in probability/partial strategies. |
| merlin/algorithms/layer.py | Binds key-aware groupings and uses the bound grouping in forward/output_keys. |
| merlin/models/init.py | Re-exports new generator-related symbols. |
| merlin/init.py | Top-level exports for new public API. |
| tests/models/test_photonic_generator.py | New comprehensive test suite for the generator pipeline. |
| tests/utils/test_grouping.py | Adds OccupancyGrouping test class covering binding, filtering, collapsing, gradients, dtype. |
| tests/measurement/test_measurement_strategy.py | Adds an integration test verifying OccupancyGrouping binds to layer output keys. |
| docs/source/api_reference/api/merlin.utils.grouping.rst | Adds OccupancyGrouping autoclass entry. |
| docs/source/api_reference/api/merlin.models.rst | Adds toctree entry for the new generator page. |
| docs/source/api_reference/api/merlin.models.photonic_generator.rst | New API reference page for the generator and adapters. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
PML-282 Add QuantumLayer-backed PhotonicGenerator
Summary
This PR adds a
PhotonicGeneratormodel that composes one or more existingQuantumLayerobjects into a latent-to-sample generator. It keeps quantum execution insideQuantumLayer, exposes raw per-layer measurements for inspection, and uses output adapters to map measurements to vectors or images.Related Issue
Related Jira: PML-282
Type of change
Proposed changes
merlin.models.PhotonicGenerator.GeneratorMeasurementsto carry raw per-layer outputs and output-key metadata.LatentDistributionNormalLatentOutputAdapterVectorAdapterImageAdaptermerlin.modelsand top-levelmerlin.docs/source/api_reference/api/merlin.models.photonic_generator.rst.API shape:
How to test / How to run
Observed locally:
Screenshots / Logs (optional)
N/A.
Performance considerations (optional)
The generator evaluates its underlying
QuantumLayerheads sequentially and then adapts the collected outputs. Runtime is therefore approximately the sum of the runtimes of the configured quantum layers. No cross-layer batching or caching is introduced in this PR.This is intentional for the first version because different generator heads may use different circuits, input states, output spaces, measurement strategies, and noise models.
Documentation
Checklist
PR title includes Jira issue key (e.g., PML-126)
"Related Jira ticket" section includes the Jira issue key (no URL)
Code formatted (ruff format)
Lint passes (ruff)
Static typing passes (mypy) if applicable
Unit tests added/updated (pytest)
Tests pass locally (pytest)
Tests pass on GPU (pytest)
Test coverage not decreased significantly
Docs build locally if affected (sphinx)
With this command:
the docs are built without any warning or errors.
New public classes/methods/packages are added in the API following the methodology presented in other files.
Dependencies updated (if needed) and pinned appropriately
PR description explains what changed and how to validate it