TRF021

Scalar tensors must be filled on-device with torch.full((), …) instead of torch.tensor(…, device=…).

   
Default Enabled
Scope All models
Source mlinter/trf021.py
Show in terminal mlinter --rule TRF021

What it does

In modeling_*.py and modular_*.py, checks calls to torch.tensor(<value>, …, device=<non-cpu>) whose <value> provably evaluates to a Python scalar. Scalar-ness is resolved statically: numeric literals, arithmetic over them, torch.finfo/torch.iinfo fields, scalar-returning builtins and math.* calls, locals bound exactly once, self.<attr> assigned in the class body, and self.config.<field>/config.<field> whose annotation in the companion configuration file is int/float/bool (following attribute_map aliases). Fields that may also be sequences, such as eos_token_id: int | list[int] | None, and any expression that cannot be resolved are left alone. Construction-time methods (__init__, _init_weights, __post_init__, post_init) are exempt because they never run inside a capture region.

Why is this bad?

torch.tensor(<python scalar>, device=<accelerator>) materialises the value on the host and then issues a host-to-device copy. CUDA graph capture forbids that copy, so the model cannot be captured. torch.full((), <value>, dtype=…, device=…) fills the same 0-d tensor directly on-device with a capturable kernel and no synchronisation.

Example

 def get_placeholder_mask(self, input_ids, inputs_embeds):
     special_image_mask = (
         inputs_embeds
         == self.get_input_embeddings()(
-            torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+            torch.full((), self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
         )
     ).all(-1)

Suppressing this rule

Add a # trf-ignore: TRF021 comment on the flagged line or the line directly above it. See Suppressing rules for whole-file directives and when a suppression is the wrong answer.