Molecular graphs, equivariance and docking

Species-conditioned self connections and parity-aware gates

HardRaw PyTorchAbout 30 min2 public examples plus private tests

NequIP

Write and run your solution Sign in

Free account with email, Google or GitHub. Your code runs in an isolated PyTorch sandbox.

Your task

Apply species-conditioned channel maps, scalar activations, and parity-aware gates to irrep features. Return updated features, activated gates, and output parities.

Function signature

equivariant_gated_update(
    activations: dict,
    parameters: dict,
    masks: dict,
    structure: dict,
    even_gate: str = 'silu',
    residual: bool = False,
) -> dict[str, torch.Tensor]

Worked example

Even and odd gates

The even scalar becomes \operatorname{SiLU}(1)\approx0.7311. The first vector has an even gate: \operatorname{sigmoid}(0)=1/2 turns [2, 4, 6] into [1, 2, 3] and preserves odd parity. The second vector has an odd gate: \tanh(1) scales every component and changes the output parity to even. No component is centered.

import torch

activations = {
    'message': torch.tensor([[[[1.0, 0.0, 0.0]], [[2.0, 4.0, 6.0]], [[2.0, 4.0, 6.0]]]], dtype=torch.float64, requires_grad=True),
    'skip': torch.full((1, 3, 1, 3), 0.0, dtype=torch.float64, requires_grad=True),
    'gates': torch.tensor([[[0.0], [0.0], [1.0]]], dtype=torch.float64, requires_grad=True),
}
parameters = {
    'w_message': torch.tensor([[[1.0]], [[1.0]], [[1.0]]], dtype=torch.float64, requires_grad=True),
    'w_self': torch.tensor([[[[0.0]], [[0.0]], [[0.0]]]], dtype=torch.float64, requires_grad=True),
    'bias': torch.tensor([[0.0], [0.0], [0.0]], dtype=torch.float64, requires_grad=True),
    'w_gate': torch.tensor([[[1.0]], [[1.0]], [[1.0]]], dtype=torch.float64, requires_grad=True),
    'gate_bias': torch.tensor([[0.0], [0.0], [0.0]], dtype=torch.float64, requires_grad=True),
    'activation_scales': torch.tensor([[[1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0]]], dtype=torch.float64, requires_grad=True),
}
masks = {
    'valid': torch.tensor([True], dtype=torch.bool),
}
structure = {
    'species': torch.tensor([0], dtype=torch.int64),
    'irreps': torch.tensor([[0, 1], [1, -1], [1, -1]], dtype=torch.int64),
    'gate_parities': torch.tensor([1, 1, -1], dtype=torch.int64),
}
even_gate = 'sigmoid'
residual = False

result = equivariant_gated_update(activations=activations, parameters=parameters, masks=masks, structure=structure, even_gate=even_gate, residual=residual)

Expected return

{
    'features': torch.tensor([[[[0.7310585786300049, 0.0, 0.0]], [[1.0, 2.0, 3.0]], [[1.5231883119115297, 3.0463766238230594, 4.569564935734589]]]], dtype=torch.float64),
    'activated_gates': torch.tensor([[[0.0], [0.5], [0.7615941559557649]]], dtype=torch.float64),
    'out_parities': torch.tensor([1, -1, 1], dtype=torch.int64),
}

Parameters

ParameterShape and meaning
activationsdictSupplied intermediate tensors.
parametersdictSupplied model parameters, keyed as shown below.
masksdictSupplied masks. Each field below defines what True includes or permits.
structuredictFields supplied together in this dictionary.
even_gatestrDefault 'silu'Choose "silu" or "sigmoid" for even scalar gates. Odd gates use the separately specified parity-compatible rule.
residualboolDefault FalseWhen True, add the supplied skip features after gating; this requires output parity to match the skip representation.

Return

dict[str, torch.Tensor]

Return exactly the named fields applicable to this operation.

Required behavior

  • For each type t, output copy o and component m, compute z_{ntom}=\sum_c message_{ntcm}\,w^{message}_{tco}+\sum_c skip_{ntcm}\,w^{self}_{species(n),tco}. Weight matrices are input-copy then output-copy; the component axis is unchanged. Add bias only for even scalars (l=0, p=+1).
  • For scalar l=0, apply \operatorname{SiLU}(z) if p=+1, \operatorname{tanh}(z) if p=-1, then multiply activation_scales [0,t].
  • For l>0, compute a scalar gate g=\mathrm{gates}\mathbin{@}w_{\mathrm{gate}}[t]; add gate_bias only if \mathrm{gate\_parities}[t]=+1. Even gates use config even_gate='silu' default or 'sigmoid'; odd gates always tanh.
  • Multiply the gate by activation_scales [1,t], then multiply every component of z by that channel's gate. The output scalar parity stays p; nonscalar output parity is p\cdot \mathrm{gate\_parities}[t].
  • Config residual=False by default; True adds raw skip after gating/scalar activation, and is legal only when all nonscalar gate parities are +1 so output irreps match. Reject incompatible residual parity. Return activated_gates zero for scalar types and masked rows.
  • Ignore component padding, invalid nodes/species, unused scalar-gate payloads and parity-forbidden bias entries, including NaNs. Global learned weights and activation scales are finite. The scales explicitly expose activation second-moment normalization instead of silently assuming library constants.
  • No arbitrary l>0 component bias is ever allowed. A packed block (l,p) has 2\cdot l+1 real components and multiplicity C; p is +1 or -1 under inversion. D is a common padded width. Padding is ignored, including NaN padding. Distinct copies of a type are channels, not XYZ coordinates.
  • Use sigmoid(z)=1/(1+e^{-z}) and SiLU(z)=z\,sigmoid(z); odd scalar/gate activations use tanh.

Why this matters

Channels can mix within an irrep, but the activation or gate parity determines whether the output still transforms as the intended type.

Starter code

You write the body of this function in the BioTorch workspace. Your code runs in an isolated PyTorch sandbox; Run checks the public examples and Submit grades the full test suite, including gradients.

import torch


def equivariant_gated_update(
    activations: dict,
    parameters: dict,
    masks: dict,
    structure: dict,
    even_gate: str = 'silu',
    residual: bool = False,
) -> dict[str, torch.Tensor]:
    """Return dict[str, torch.Tensor]:
    
    features: (N, T, C, D).
    activated_gates: (N, T, C).
    out_parities: (T,)."""
    # Implement the operation. Inputs and test setup are supplied by BioTorch.
    raise NotImplementedError("Implement equivariant_gated_update")

Ready to implement it?

3 progressive hints, a solution walkthrough, and grading that checks values, shapes and gradients are waiting in the workspace.

Write and run your solution Sign in

Free account with email, Google or GitHub. Your code runs in an isolated PyTorch sandbox.

Sources and further reading

A primitive exercise, not an end-to-end reproduction of the named models. No affiliation or endorsement is implied.