Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy of the inputs for the evaluation during compilation #1150

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion forge/forge/tvm_to_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -2071,7 +2071,8 @@ def generate_forge_module(
reload = False

if verify_cfg is not None and verify_cfg.verify_forge_codegen_vs_framework:
framework_outputs = framework_mod.cpu_eval_forward(*pytorch_inputs)
eval_inputs = [eval_input.detach().clone() for eval_input in pytorch_inputs]
framework_outputs = framework_mod.cpu_eval_forward(*eval_inputs)

if not reload:
module_name = graph_name if counter == 0 else f"{graph_name}_{counter}"
Expand Down
116 changes: 116 additions & 0 deletions forge/test/mlir/operators/tm/test_in_place.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# SPDX-FileCopyrightText: (c) 2025 Tenstorrent AI ULC
#
# SPDX-License-Identifier: Apache-2.0
import pytest
import torch
import tensorflow as tf

import forge
from forge.verify.compare import compare_with_golden


@pytest.mark.parametrize(
"shape",
[
(3, 3),
],
)
@pytest.mark.push
def test_in_place_torch(shape):
class Inplace(torch.nn.Module):
def __init__(self):
super().__init__()

def forward(self, x):
y = x + 1
x += 2

return x + y

input = torch.zeros(shape, requires_grad=False)
framework_input = input.detach().clone()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we're doing detach and clone here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because if run inference on the torch module and then want to run it on compiled module, inputs for the compiled module would be different due to the changes made by torch (as default torch is not functional, meaning it will change input)

tt_inputs = [input]

framework_model = Inplace()
y = framework_model(framework_input)

compiled_model = forge.compile(framework_model, sample_inputs=tt_inputs, module_name="inplace")
tty = compiled_model(*tt_inputs)[0]

compare_with_golden(golden=y, calculated=tty)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the standard verify function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the nature of the in-place problem we are facing, I have separate input for compiled module and torch module so I can't pass it to the verify function.

print(framework_input)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need these prints?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really.

print(tt_inputs[0])


@pytest.mark.parametrize(
"shape",
[
(3, 3),
],
)
@pytest.mark.push
def test_in_place_tf(shape):
class Inplace(tf.keras.Model):
def __init__(self):
super().__init__()

def call(self, x):
y = x + 1
x += 2
return x + y

input = tf.zeros(shape)
framework_input = tf.identity(input)
tt_inputs = [input]

framework_model = Inplace()
y = framework_model(framework_input)

compiled_model = forge.compile(framework_model, sample_inputs=tt_inputs, module_name="inplace")
tty = compiled_model(*tt_inputs)[0]

# convert tensor from tf to torch
y = torch.tensor(y.numpy())

compare_with_golden(golden=y, calculated=tty)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comments as for PT example


print(framework_input)
print(tt_inputs[0])


@pytest.mark.parametrize(
"shape",
[
(1, 1024),
],
)
@pytest.mark.push
@pytest.mark.xfail(
reason="TT model silently computes the result for the tensor with requires_grad=True on which in-place operation has been performed"
)
def test_in_place_backward(shape):
class MatmulParam(torch.nn.Module):
def __init__(self):
super().__init__()
self.p = torch.nn.Parameter(torch.rand(1024, 1024))
torch.nn.init.xavier_uniform_(self.p)

def forward(self, x):
y = torch.matmul(x, self.p)
x += 2 # In-place modification causing runtime error
return y

inputs = torch.rand(shape, requires_grad=True)

model = MatmulParam()
tt_model = forge.compile(model, sample_inputs=[torch.rand(shape)])

# Expect PyTorch model to raise a RuntimeError
with pytest.raises(
RuntimeError, match="a leaf Variable that requires grad is being used in an in-place operation."
):
model(inputs)

# Expect tt_model to also raise a RuntimeError
with pytest.raises(RuntimeError):
tt_model(inputs) # If no error is raised, the test will fail
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_mlp_mixer_timm_pytorch(record_forge_property, variant):
record_forge_property("model_name", module_name)

framework_model = download_model(timm.create_model, variant, pretrained=True)
config = resolve_data_config({}, model=model)
config = resolve_data_config({}, model=framework_model)
transform = create_transform(**config)

try:
Expand Down
Loading