Skip to content

pytorch lightning Fail #263

Open
Open

Description

@niitsuma

Following code stop with

AttributeError: 'builtin_function_or_method' object has no attribute 'code'. Did you mean: 'call'?

(ns pytorchlightning.core
  (:gen-class)

  (:require 
   [libpython-clj2.python :as py
    :refer
    [ py. py.- 
     as-jvm
     set-attr!
     get-item
     ->py-tuple
     ->py-list
     ]]
   
   [libpython-clj2.require :refer [require-python]]
   ))

;;(py/initialize!)

(require-python
 '[builtins :as python]
 '[torch]
 '[torch.nn :as nn :refer [Linear]]
 '[torch.nn.functional :refer [mse_loss]]
 '[torch.utils.data :refer [DataLoader Dataset]]
 '[torch.optim]
 '[pytorch_lightning]
 )


(defonce model (atom nil))

(def LitModel
  (py/create-class
   "LitModel" [pytorch_lightning/LightningModule]
   {"__init__"
    (py/make-tuple-instance-fn
     (fn [self]
       (py. pytorch_lightning/LightningModule  __init__ self)
       (py/set-attr!  self "layer" (Linear 1 1))
       nil))
    
    "forward"
    (py/make-tuple-instance-fn
     (fn [self x] (py. self layer x))
     :arg-converter as-jvm
     :method-name "forward"
     )
    
    "training_step"
    (py/make-tuple-instance-fn
     (fn [self batch batch_idx]
       (mse_loss (py/get-item batch 1) (py. self forward (py/get-item batch 0)))
       ))
    "validation_step"
    (py/make-tuple-instance-fn
     (fn [self batch batch_idx]
       (mse_loss (py/get-item batch 1) (py. self forward (py/get-item batch 0)))
       ))
    "test_step"
    (py/make-tuple-instance-fn
     (fn [self batch batch_idx]
       (mse_loss (py/get-item batch 1) (py. self forward (py/get-item batch 0)))
       ))
    
    "configure_optimizers"
    (py/make-tuple-instance-fn
     (fn [self]
       (torch.optim/SGD
        (py. self parameters)
        :lr 0.02)
       ))
    }))

(def SimpleDataset
  (py/create-class
   "SimpleDataset" [Dataset]
   {"__init__"
    (py/make-tuple-instance-fn
     (fn [self data] (py/set-attr! self "data" data)  nil))
    "__len__"
    (py/make-tuple-instance-fn 
     (fn [self] (python/len (py.- self data))))
    "__getitem__"
    (py/make-tuple-instance-fn
     (fn [self idx]
       (py/->py-tuple
        [
         (torch/tensor [(py/get-item  (py/get-item (py.- self data) idx) 0)] :dtype torch/float32)
         (torch/tensor [(py/get-item  (py/get-item (py.- self data) idx) 1)] :dtype torch/float32)
         ]
        )
       ))
    }))


(def SimpleDataModule
  (py/create-class
   "SimpleDataModule" [pytorch_lightning/LightningDataModule]
   {"__init__"
    (py/make-tuple-instance-fn
     (fn [self data] (py/set-attr! self "data" data)  nil))
    
    "train_dataloader"
    (py/make-tuple-instance-fn
     (fn [self]
       (DataLoader (SimpleDataset (py.- self data )) :batch_size 2 :shuffle false)))
    "val_dataloader"
    (py/make-tuple-instance-fn
     (fn [self]
       (DataLoader (SimpleDataset (py.- self data )) :batch_size 2 :shuffle false)))
    "test_dataloader"
    (py/make-tuple-instance-fn
     (fn [self]
       (DataLoader (SimpleDataset (py.- self data )) :batch_size 2 :shuffle false)))
    }))




(defn -main [& args]

  (reset! model (LitModel))

  (def data [[1.0 3.0] [2.0 5.0] [3.0 7.0] [4.0 9.0] [5.0 11.0]] )
  (def data_pylist (py/->py-list data))

  ;; (println  (py/get-item  (py/get-item data_pylist 0) 0))

  (def train_dataset  (SimpleDataset data_pylist))
  (def train_loader (DataLoader train_dataset :batch_size 2 :shuffle false))

  ;; ;;for debug
  ;; (def train_pylist (python/list train_loader))
  ;; (def train_pylist_0 (py/get-item train_pylist 0))
  ;; (println (py. @model training_step train_pylist_0 0) );;;OK.  maybe this part work

  
  (def trainer (pytorch_lightning/Trainer :max_epochs 10 ))

  (def datamodu (SimpleDataModule data_pylist))
  
  (py. trainer fit @model train_loader)
  ;;(py. trainer fit @model datamodu)  ;;also fail

  )

Activity

  1. cnuernber commented on Sep 14, 2024

    @cnuernber
    Collaborator

    It appears to me that this happens when you pass in a function that doesn't have its source code thus the autodiff system can't autodiff. Keep in mind that functions defined via clojure are translated to python as opaque C function pointers so they won't be autodifferentiable.

  2. tani commented on Feb 3, 2025

    @tani

    @cnuernber Thank you for the awesome project!
    __code__ is used in the following parts, and it is likely only to check whether instance_method has been overridden as intended. Now, I would be happy if there were a way to give a fake __code__ value to a function represented by a C pointer—something like (set-attr! func "__code__" ""). Do you have any good ideas?

    https://github.com/Lightning-AI/utilities/blob/b2359648d26af67d5f97fd8e00c47103c976a485/src/lightning_utilities/core/overrides.py#L9-L34

    https://github.com/Lightning-AI/torchmetrics/blob/c57197e7e68e226aefb28f4496a751555074c352/src/torchmetrics/utilities/checks.py#L740-L762

    Execution error at libpython-clj2.python.ffi/check-error-throw (ffi.clj:717).
    Traceback (most recent call last):
      File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/trainer.py", line 539, in fit
        call._call_and_handle_interrupt(
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
            self, self._fit_impl, model, train_dataloaders, val_dataloaders, datamodule, ckpt_path
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        )
        ^
      File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/call.py", line 47, in _call_and_handle_interrupt
        return trainer_fn(*args, **kwargs)
      File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/trainer.py", line 575, in _fit_impl
        self._run(model, ckpt_path=ckpt_path)
        ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/trainer.py", line 932, in _run
        _verify_loop_configurations(self)
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
      File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/configuration_validator.py", line 36, in _verify_loop_configurations
        __verify_train_val_loop_configuration(trainer, model)
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
      File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/configuration_validator.py", line 51, in __verify_train_val_loop_configuration
        has_training_step = is_overridden("training_step", model)
      File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/utilities/model_helpers.py", line 46, in is_overridden
        return _is_overridden(method_name, instance, parent)
      File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning_utilities/core/overrides.py", line 34, in is_overridden
        return instance_attr.__code__ != parent_attr.__code__
               ^^^^^^^^^^^^^^^^^^^^^^
    AttributeError: 'builtin_function_or_method' object has no attribute '__code__'. Did you mean: '__call__'?
    
    (ns sample.core
      (:require
       [libpython-clj2.require :refer [require-python]]
       [libpython-clj2.python :as py :refer [py. py.- py.. py* py**]]
       ))
    
    (py/initialize! :python-executable "/Users/tani/Documents/libpython-clj/.venv/bin/python")
    
    (require-python
     'os
     '[torch.nn :refer [Linear ReLU Sequential]]
     '[torch.nn.functional :refer [mse_loss]]
     '[torch.optim :as o :refer [Adam]]
     '[torch.utils.data :refer [DataLoader]]
     '[lightning :refer [LightningModule Trainer]]
     '[torchvision.datasets :refer [MNIST]]
     '[torchvision.transforms :refer [ToTensor]])
    
    (def encoder (Sequential (Linear (* 28 28) 64) (ReLU) (Linear 64 3)))
    (def decoder (Sequential (Linear 64 3) (ReLU)  (Linear (* 28 28) 64)))
    (def LitModel
      (py/create-class
       "LitModel" [LightningModule]
       {"__init__"
        (py/make-tuple-instance-fn
         (fn [self encoder decoder]
           (py. LightningModule __init__ self)
           (py/set-attr! self "encoder" encoder)
           (py/set-attr! self "decoder" decoder)
           nil))
        "training_step"
        (py/make-tuple-instance-fn
         (fn [self batch batch_idx]
           (let [head (py/get-item batch 0)
                 x (py. head view (py. head size 0) -1)
                 z (py. self encoder x)
                 xhat (py. self decoder z)
                 loss (mse_loss xhat x)]
             loss)))
        "configure_optimizers"
        (py/make-tuple-instance-fn
         (fn [self]
           (py** Adam (py. self parameters) {:lr 0.0001})))}))
    
    (def model (LitModel encoder decoder))
    (def dataset (MNIST (os/getcwd) :download true :transform (ToTensor)))
    (def train_loader (DataLoader dataset))
    (def trainer (Trainer :limit_train_batches 2 :max_epochs 1 :logger []))
    (py. trainer fit (py/as-python model) train_loader)
  3. amano-kenji commented on Mar 28, 2025

    @amano-kenji

    I asked grok chatbot about this. It told me to create a subclass of torch.autograd.Function with libpython-clj as below.

    import torch
    from torch.autograd import Function
    from ctypes import cdll
    
    # Load the C library
    lib = cdll.LoadLibrary("./mylib.so")
    lib.double_value.argtypes = [ctypes.c_float]
    lib.double_value.restype = ctypes.c_float
    
    # Custom autograd function
    class DoubleFunction(Function):
        @staticmethod
        def forward(ctx, input):
            # Extract scalar value from tensor, call C function
            scalar_input = input.item()
            result = lib.double_value(scalar_input)
            # Return as a tensor
            output = torch.tensor([result], dtype=input.dtype, device=input.device)
            # Save input for backward pass if needed
            ctx.save_for_backward(input)
            return output
    
        @staticmethod
        def backward(ctx, grad_output):
            # Retrieve saved input if needed (here, not strictly necessary)
            input, = ctx.saved_tensors
            # Gradient of doubling is 2, multiplied by incoming gradient
            grad_input = grad_output * 2
            return grad_input
    
    # Python wrapper for convenience
    def double_tensor(x):
        return DoubleFunction.apply(x)
    

    Another way is to replace pytorch lightning with pytorch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions