torchcell.models.equivariant_cell_graph_transformer.EquivariantPerturbationTransform
- class EquivariantPerturbationTransform(hidden_dim: int, num_heads: int = 8, dropout: float = 0.1, residual: str = 'postln', num_layers: int = 1, ffn_mult: int = 4, extra_layer_ffn_mult: int | None = None, hadamard: str = 'off', null_sink: bool = False, null_sink_bias_init: float = -4.0, null_sink_trainable: bool = True, null_sink_magnitude_match: bool = False)[source]
Bases:
ModuleEquivariant perturbation transformation that preserves per-gene structure.
Unlike the standard perturbation head which collapses to a summary vector, this module transforms ALL gene embeddings based on perturbation context, maintaining the [batch, N, d] shape for downstream equivariant tasks.
Implements Type I Virtual Instrument: H_genes → H_genes_pert
- forward(H_genes: Tensor, perturbation_indices: Tensor, batch_assignment: Tensor, batch_size: int) tuple[Tensor, Tensor][source]
Apply equivariant perturbation transformation.
- Parameters:
H_genes – [N, d] - wildtype gene embeddings
perturbation_indices – [total_pert_genes] - indices of perturbed genes
batch_assignment – [total_pert_genes] - batch index for each perturbed gene
batch_size – Number of genotypes in the batch. Passed explicitly (the model reads
batch.num_graphs) because a genotype with no perturbed gene has no entry inbatch_assignment:max + 1would drop a trailing one. A genotype with no entry gets a zero context at every layer, so its row is the layer stack applied withattended = 0.
- Returns:
H_genes_pert: [batch, N, d] - perturbed gene embeddings (EQUIVARIANT!)context: [batch, N, d] - the attended perturbation context, returned separately so downstream heads can condition on it directly (the concat/bilinear/FiLM arms all need c_b, not only h_i + c_b).
- Return type:
A tuple
(H_genes_pert, context)