Skip to content
Merged
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
75 changes: 75 additions & 0 deletions docs/faq/class_methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Calling Python Class Methods Inside a Kernel

## The problem

If you try to call a standard Python class method, such as
`self.get_value()`, from inside a function decorated with
`@basic` or `@structural`, the compiler will fail. Historically, before Kirin
v0.16.8, this could appear as an `AttributeError` about `arg_names`.

## Why this happens

A kernel is a piece of code that describes instructions intended to run on the
target device, not on your host machine. Because of this, the Kirin compiler
cannot evaluate arbitrary Python runtime expressions, such as object attribute
lookups or dynamically calling class methods, during device execution.

The compiler only parses global constants and already-resolved kernel functions.

## The solution

Resolve the Python method on the host before defining the kernel. By assigning
the result to a local variable, the inner kernel can cleanly capture that
variable as a constant.

### Incorrect

This tries to evaluate the method inside the device kernel.

```python
from kirin.prelude import basic


class Offset:
def __init__(self, value: int):
self.value = value

def get_value(self) -> int:
return self.value

def bad_method(self):
@basic
def add_offset(x: int) -> int:
# ERROR: Kirin cannot evaluate arbitrary Python class methods.
return x + self.get_value()

return add_offset
```

### Correct

Resolve the method on the host side, then capture it in the kernel closure.

```python
from kirin.prelude import basic


class Offset:
def __init__(self, value: int):
self.value = value

def get_value(self) -> int:
return self.value

def method(self):
# 1. Resolve the method in host Python code.
offset = self.get_value()

# 2. Define the device kernel.
@basic
def add_offset(x: int) -> int:
# 3. Use the captured constant inside the kernel.
return x + offset

return add_offset
```
9 changes: 9 additions & 0 deletions docs/faq/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# FAQ

This page collects common questions with a short answer and a link to a deeper explanation.

## Kernel authoring

| Question | Short answer |
| --- | --- |
| [Why do I get an `AttributeError` when calling a class method inside a kernel?](class_methods.md) | Resolve the Python method on the host side before defining the kernel, then capture it as a constant. |
3 changes: 3 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ nav:
- Structural Control Flow: dialects/scf.md
- Immutable List: dialects/ilist.md
- Compiler 101: 101.md
- FAQ:
- faq/index.md
- Class methods in kernels: faq/class_methods.md
# - Comparison: comparison.md
- Contributing: contrib.md
- Cookbook:
Expand Down
Loading