diff --git a/candle-examples/Cargo.toml b/candle-examples/Cargo.toml index 864d2f6a..5b90f140 100644 --- a/candle-examples/Cargo.toml +++ b/candle-examples/Cargo.toml @@ -25,7 +25,7 @@ hf-hub = { workspace = true, features = ["tokio"] } image = { workspace = true } intel-mkl-src = { workspace = true, optional = true } num-traits = { workspace = true } -pyo3 = { version = "0.20.0", features = ["auto-initialize"], optional = true } +pyo3 = { version = "0.21.0", features = ["auto-initialize"], optional = true } rayon = { workspace = true } rubato = { version = "0.15.0", optional = true } safetensors = { workspace = true } diff --git a/candle-examples/examples/reinforcement-learning/gym_env.rs b/candle-examples/examples/reinforcement-learning/gym_env.rs index 8868c188..a2b6652f 100644 --- a/candle-examples/examples/reinforcement-learning/gym_env.rs +++ b/candle-examples/examples/reinforcement-learning/gym_env.rs @@ -42,7 +42,7 @@ impl GymEnv { /// Creates a new session of the specified OpenAI Gym environment. pub fn new(name: &str) -> Result { Python::with_gil(|py| { - let gym = py.import("gymnasium")?; + let gym = py.import_bound("gymnasium")?; let make = gym.getattr("make")?; let env = make.call1((name,))?; let action_space = env.getattr("action_space")?; @@ -66,10 +66,10 @@ impl GymEnv { /// Resets the environment, returning the observation tensor. pub fn reset(&self, seed: u64) -> Result { let state: Vec = Python::with_gil(|py| { - let kwargs = PyDict::new(py); + let kwargs = PyDict::new_bound(py); kwargs.set_item("seed", seed)?; - let state = self.env.call_method(py, "reset", (), Some(kwargs))?; - state.as_ref(py).get_item(0)?.extract() + let state = self.env.call_method_bound(py, "reset", (), Some(&kwargs))?; + state.bind(py).get_item(0)?.extract() }) .map_err(w)?; Tensor::new(state, &Device::Cpu) @@ -81,8 +81,10 @@ impl GymEnv { action: A, ) -> Result> { let (state, reward, terminated, truncated) = Python::with_gil(|py| { - let step = self.env.call_method(py, "step", (action.clone(),), None)?; - let step = step.as_ref(py); + let step = self + .env + .call_method_bound(py, "step", (action.clone(),), None)?; + let step = step.bind(py); let state: Vec = step.get_item(0)?.extract()?; let reward: f64 = step.get_item(1)?.extract()?; let terminated: bool = step.get_item(2)?.extract()?; diff --git a/candle-examples/examples/reinforcement-learning/vec_gym_env.rs b/candle-examples/examples/reinforcement-learning/vec_gym_env.rs index 8f8f30bd..e382ad76 100644 --- a/candle-examples/examples/reinforcement-learning/vec_gym_env.rs +++ b/candle-examples/examples/reinforcement-learning/vec_gym_env.rs @@ -24,13 +24,13 @@ fn w(res: PyErr) -> candle::Error { impl VecGymEnv { pub fn new(name: &str, img_dir: Option<&str>, nprocesses: usize) -> Result { Python::with_gil(|py| { - let sys = py.import("sys")?; + let sys = py.import_bound("sys")?; let path = sys.getattr("path")?; let _ = path.call_method1( "append", ("candle-examples/examples/reinforcement-learning",), )?; - let gym = py.import("atari_wrappers")?; + let gym = py.import_bound("atari_wrappers")?; let make = gym.getattr("make")?; let env = make.call1((name, img_dir, nprocesses))?; let action_space = env.getattr("action_space")?; @@ -60,10 +60,10 @@ impl VecGymEnv { pub fn step(&self, action: Vec) -> Result { let (obs, reward, is_done) = Python::with_gil(|py| { - let step = self.env.call_method(py, "step", (action,), None)?; - let step = step.as_ref(py); + let step = self.env.call_method_bound(py, "step", (action,), None)?; + let step = step.bind(py); let obs = step.get_item(0)?.call_method("flatten", (), None)?; - let obs_buffer = pyo3::buffer::PyBuffer::get(obs)?; + let obs_buffer = pyo3::buffer::PyBuffer::get_bound(&obs)?; let obs: Vec = obs_buffer.to_vec(py)?; let reward: Vec = step.get_item(1)?.extract()?; let is_done: Vec = step.get_item(2)?.extract()?; diff --git a/candle-pyo3/Cargo.toml b/candle-pyo3/Cargo.toml index 7c6fbd68..88001334 100644 --- a/candle-pyo3/Cargo.toml +++ b/candle-pyo3/Cargo.toml @@ -20,10 +20,10 @@ candle-nn = { workspace = true } candle-onnx = { workspace = true, optional = true } half = { workspace = true } intel-mkl-src = { workspace = true, optional = true } -pyo3 = { version = "0.20.0", features = ["extension-module", "abi3-py38"] } +pyo3 = { version = "0.21.0", features = ["extension-module", "abi3-py38"] } [build-dependencies] -pyo3-build-config = "0.20" +pyo3-build-config = "0.21" [features] default = [] diff --git a/candle-pyo3/py_src/candle/nn/__init__.pyi b/candle-pyo3/py_src/candle/nn/__init__.pyi new file mode 100644 index 00000000..118c4cff --- /dev/null +++ b/candle-pyo3/py_src/candle/nn/__init__.pyi @@ -0,0 +1,19 @@ +# Generated content DO NOT EDIT +from typing import Any, Callable, Dict, List, Optional, Tuple, Union, Sequence +from os import PathLike +from candle.typing import _ArrayLike, Device, Scalar, Index, Shape +from candle import Tensor, DType, QTensor + +@staticmethod +def silu(tensor: Tensor) -> Tensor: + """ + Applies the Sigmoid Linear Unit (SiLU) function to a given tensor. + """ + pass + +@staticmethod +def softmax(tensor: Tensor, dim: int) -> Tensor: + """ + Applies the Softmax function to a given tensor.# + """ + pass diff --git a/candle-pyo3/src/lib.rs b/candle-pyo3/src/lib.rs index e0d3bf30..0da2c700 100644 --- a/candle-pyo3/src/lib.rs +++ b/candle-pyo3/src/lib.rs @@ -60,8 +60,8 @@ impl PyDType { impl PyDType { fn from_pyobject(ob: PyObject, py: Python<'_>) -> PyResult { use std::str::FromStr; - if let Ok(dtype) = ob.extract::<&str>(py) { - let dtype = DType::from_str(dtype) + if let Ok(dtype) = ob.extract::(py) { + let dtype = DType::from_str(&dtype) .map_err(|_| PyTypeError::new_err(format!("invalid dtype '{dtype}'")))?; Ok(Self(dtype)) } else { @@ -116,8 +116,8 @@ impl PyDevice { impl<'source> FromPyObject<'source> for PyDevice { fn extract(ob: &'source PyAny) -> PyResult { - let device: &str = ob.extract()?; - let device = match device { + let device: String = ob.extract()?; + let device = match device.as_str() { "cpu" => PyDevice::Cpu, "cuda" => PyDevice::Cuda, _ => Err(PyTypeError::new_err(format!("invalid device '{device}'")))?, @@ -265,7 +265,7 @@ impl PyTensor { } else if let Ok(TorchTensor(numpy)) = data.extract::(py) { return PyTensor::new(py, numpy); } else { - let ty = data.as_ref(py).get_type(); + let ty = data.bind(py).get_type(); Err(PyTypeError::new_err(format!( "incorrect type {ty} for tensor" )))? @@ -322,7 +322,7 @@ impl PyTensor { fn to_torch(&self, py: Python<'_>) -> PyResult { let candle_values = self.values(py)?; let torch_tensor: PyObject = py - .import("torch")? + .import_bound("torch")? .getattr("tensor")? .call1((candle_values,))? .extract()?; @@ -333,7 +333,7 @@ impl PyTensor { /// Gets the tensor's shape. /// &RETURNS&: Tuple[int] fn shape(&self, py: Python<'_>) -> PyObject { - PyTuple::new(py, self.0.dims()).to_object(py) + PyTuple::new_bound(py, self.0.dims()).to_object(py) } #[getter] @@ -347,7 +347,7 @@ impl PyTensor { /// Gets the tensor's strides. /// &RETURNS&: Tuple[int] fn stride(&self, py: Python<'_>) -> PyObject { - PyTuple::new(py, self.0.stride()).to_object(py) + PyTuple::new_bound(py, self.0.stride()).to_object(py) } #[getter] @@ -527,7 +527,7 @@ impl PyTensor { } fn extract_indexer( - py_indexer: &PyAny, + py_indexer: &Bound, current_dim: usize, dims: &[usize], index_argument_count: usize, @@ -567,7 +567,7 @@ impl PyTensor { ), current_dim + 1, )) - } else if py_indexer.is_ellipsis() { + } else if py_indexer.is(&py_indexer.py().Ellipsis()) { // Handle '...' e.g. tensor[..., 0] if current_dim > 0 { return Err(PyTypeError::new_err( @@ -586,7 +586,7 @@ impl PyTensor { } } - if let Ok(tuple) = idx.downcast::(py) { + if let Ok(tuple) = idx.downcast_bound::(py) { let not_none_count: usize = tuple.iter().filter(|x| !x.is_none()).count(); if not_none_count > dims.len() { @@ -596,12 +596,12 @@ impl PyTensor { let mut current_dim = 0; for item in tuple.iter() { let (indexer, new_current_dim) = - extract_indexer(item, current_dim, dims, not_none_count)?; + extract_indexer(&item, current_dim, dims, not_none_count)?; current_dim = new_current_dim; indexers.push(indexer); } } else { - let (indexer, _) = extract_indexer(idx.downcast::(py)?, 0, dims, 1)?; + let (indexer, _) = extract_indexer(idx.downcast_bound::(py)?, 0, dims, 1)?; indexers.push(indexer); } @@ -652,7 +652,7 @@ impl PyTensor { /// Add two tensors. /// &RETURNS&: Tensor - fn __add__(&self, rhs: &PyAny) -> PyResult { + fn __add__(&self, rhs: &Bound) -> PyResult { let tensor = if let Ok(rhs) = rhs.extract::() { self.0.broadcast_add(&rhs.0).map_err(wrap_err)? } else if let Ok(rhs) = rhs.extract::() { @@ -663,13 +663,13 @@ impl PyTensor { Ok(Self(tensor)) } - fn __radd__(&self, rhs: &PyAny) -> PyResult { + fn __radd__(&self, rhs: &Bound) -> PyResult { self.__add__(rhs) } /// Multiply two tensors. /// &RETURNS&: Tensor - fn __mul__(&self, rhs: &PyAny) -> PyResult { + fn __mul__(&self, rhs: &Bound) -> PyResult { let tensor = if let Ok(rhs) = rhs.extract::() { self.0.broadcast_mul(&rhs.0).map_err(wrap_err)? } else if let Ok(rhs) = rhs.extract::() { @@ -680,13 +680,13 @@ impl PyTensor { Ok(Self(tensor)) } - fn __rmul__(&self, rhs: &PyAny) -> PyResult { + fn __rmul__(&self, rhs: &Bound) -> PyResult { self.__mul__(rhs) } /// Subtract two tensors. /// &RETURNS&: Tensor - fn __sub__(&self, rhs: &PyAny) -> PyResult { + fn __sub__(&self, rhs: &Bound) -> PyResult { let tensor = if let Ok(rhs) = rhs.extract::() { self.0.broadcast_sub(&rhs.0).map_err(wrap_err)? } else if let Ok(rhs) = rhs.extract::() { @@ -699,7 +699,7 @@ impl PyTensor { /// Divide two tensors. /// &RETURNS&: Tensor - fn __truediv__(&self, rhs: &PyAny) -> PyResult { + fn __truediv__(&self, rhs: &Bound) -> PyResult { let tensor = if let Ok(rhs) = rhs.extract::() { self.0.broadcast_div(&rhs.0).map_err(wrap_err)? } else if let Ok(rhs) = rhs.extract::() { @@ -711,7 +711,7 @@ impl PyTensor { } /// Rich-compare two tensors. /// &RETURNS&: Tensor - fn __richcmp__(&self, rhs: &PyAny, op: CompareOp) -> PyResult { + fn __richcmp__(&self, rhs: &Bound, op: CompareOp) -> PyResult { let compare = |lhs: &Tensor, rhs: &Tensor| { let t = match op { CompareOp::Eq => lhs.eq(rhs), @@ -957,7 +957,7 @@ impl PyTensor { #[pyo3(signature = (*args, **kwargs), text_signature = "(self, *args, **kwargs)")] /// Performs Tensor dtype and/or device conversion. /// &RETURNS&: Tensor - fn to(&self, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult { + fn to(&self, args: &Bound, kwargs: Option<&Bound>) -> PyResult { let mut device: Option = None; let mut dtype: Option = None; let mut other: Option = None; @@ -1227,7 +1227,7 @@ impl PyQTensor { ///Gets the shape of the tensor. /// &RETURNS&: Tuple[int] fn shape(&self, py: Python<'_>) -> PyObject { - PyTuple::new(py, self.0.shape().dims()).to_object(py) + PyTuple::new_bound(py, self.0.shape().dims()).to_object(py) } fn __repr__(&self) -> String { @@ -1265,7 +1265,7 @@ fn load_safetensors(path: &str, py: Python<'_>) -> PyResult { .into_iter() .map(|(key, value)| (key, PyTensor(value).into_py(py))) .collect::>(); - Ok(res.into_py_dict(py).to_object(py)) + Ok(res.into_py_dict_bound(py).to_object(py)) } #[pyfunction] @@ -1303,7 +1303,7 @@ fn load_ggml( .map(|(key, qtensor)| Ok((key, PyQTensor(Arc::new(qtensor)).into_py(py)))) .collect::<::candle::Result>>() .map_err(wrap_err)?; - let tensors = tensors.into_py_dict(py).to_object(py); + let tensors = tensors.into_py_dict_bound(py).to_object(py); let hparams = [ ("n_vocab", ggml.hparams.n_vocab), ("n_embd", ggml.hparams.n_embd), @@ -1313,7 +1313,7 @@ fn load_ggml( ("n_rot", ggml.hparams.n_rot), ("ftype", ggml.hparams.ftype), ]; - let hparams = hparams.into_py_dict(py).to_object(py); + let hparams = hparams.into_py_dict_bound(py).to_object(py); let vocab = ggml .vocab .token_score_pairs @@ -1351,7 +1351,7 @@ fn load_gguf( gguf_file::Value::Bool(x) => x.into_py(py), gguf_file::Value::String(x) => x.into_py(py), gguf_file::Value::Array(x) => { - let list = pyo3::types::PyList::empty(py); + let list = pyo3::types::PyList::empty_bound(py); for elem in x.iter() { list.append(gguf_value_to_pyobject(elem, py)?)?; } @@ -1371,13 +1371,13 @@ fn load_gguf( }) .collect::<::candle::Result>>() .map_err(wrap_err)?; - let tensors = tensors.into_py_dict(py).to_object(py); + let tensors = tensors.into_py_dict_bound(py).to_object(py); let metadata = gguf .metadata .iter() .map(|(key, value)| Ok((key, gguf_value_to_pyobject(value, py)?))) .collect::>>()? - .into_py_dict(py) + .into_py_dict_bound(py) .to_object(py); Ok((tensors, metadata)) } @@ -1390,7 +1390,7 @@ fn load_gguf( fn save_gguf(path: &str, tensors: PyObject, metadata: PyObject, py: Python<'_>) -> PyResult<()> { use ::candle::quantized::gguf_file; - fn pyobject_to_gguf_value(v: &PyAny, py: Python<'_>) -> PyResult { + fn pyobject_to_gguf_value(v: &Bound, py: Python<'_>) -> PyResult { let v: gguf_file::Value = if let Ok(x) = v.extract::() { gguf_file::Value::U8(x) } else if let Ok(x) = v.extract::() { @@ -1418,7 +1418,7 @@ fn save_gguf(path: &str, tensors: PyObject, metadata: PyObject, py: Python<'_>) } else if let Ok(x) = v.extract::>() { let x = x .into_iter() - .map(|f| pyobject_to_gguf_value(f.as_ref(py), py)) + .map(|f| pyobject_to_gguf_value(f.bind(py), py)) .collect::>>()?; gguf_file::Value::Array(x) } else { @@ -1450,7 +1450,7 @@ fn save_gguf(path: &str, tensors: PyObject, metadata: PyObject, py: Python<'_>) Ok(( key.extract::() .map_err(|_| PyErr::new::("keys must be strings"))?, - pyobject_to_gguf_value(value, py)?, + pyobject_to_gguf_value(&value.as_borrowed(), py)?, )) }) .collect::>>()?; @@ -1498,7 +1498,7 @@ fn get_num_threads() -> usize { ::candle::utils::get_num_threads() } -fn candle_utils(_py: Python<'_>, m: &PyModule) -> PyResult<()> { +fn candle_utils(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(cuda_is_available, m)?)?; m.add_function(wrap_pyfunction!(get_num_threads, m)?)?; m.add_function(wrap_pyfunction!(has_accelerate, m)?)?; @@ -1579,7 +1579,7 @@ fn tanh(tensor: PyTensor) -> PyResult { Ok(PyTensor(s)) } -fn candle_functional_m(_py: Python<'_>, m: &PyModule) -> PyResult<()> { +fn candle_functional_m(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(silu, m)?)?; m.add_function(wrap_pyfunction!(softmax, m)?)?; m.add_function(wrap_pyfunction!(max_pool2d, m)?)?; @@ -1591,7 +1591,7 @@ fn candle_functional_m(_py: Python<'_>, m: &PyModule) -> PyResult<()> { } #[cfg(feature = "onnx")] -fn candle_onnx_m(_py: Python<'_>, m: &PyModule) -> PyResult<()> { +fn candle_onnx_m(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { use onnx::{PyONNXModel, PyONNXTensorDescriptor}; m.add_class::()?; m.add_class::()?; @@ -1599,18 +1599,18 @@ fn candle_onnx_m(_py: Python<'_>, m: &PyModule) -> PyResult<()> { } #[pymodule] -fn candle(py: Python<'_>, m: &PyModule) -> PyResult<()> { - let utils = PyModule::new(py, "utils")?; - candle_utils(py, utils)?; - m.add_submodule(utils)?; - let nn = PyModule::new(py, "functional")?; - candle_functional_m(py, nn)?; - m.add_submodule(nn)?; +fn candle(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { + let utils = PyModule::new_bound(py, "utils")?; + candle_utils(py, &utils)?; + m.add_submodule(&utils)?; + let nn = PyModule::new_bound(py, "functional")?; + candle_functional_m(py, &nn)?; + m.add_submodule(&nn)?; #[cfg(feature = "onnx")] { - let onnx = PyModule::new(py, "onnx")?; - candle_onnx_m(py, onnx)?; - m.add_submodule(onnx)?; + let onnx = PyModule::new_bound(py, "onnx")?; + candle_onnx_m(py, &onnx)?; + m.add_submodule(&onnx)?; } m.add_class::()?; m.add_class::()?; diff --git a/candle-pyo3/src/onnx.rs b/candle-pyo3/src/onnx.rs index b9a0eb22..a2e9a087 100644 --- a/candle-pyo3/src/onnx.rs +++ b/candle-pyo3/src/onnx.rs @@ -39,7 +39,7 @@ impl PyONNXTensorDescriptor { /// The shape of the tensor. /// &RETURNS&: Tuple[Union[int,str,Any]] fn shape(&self, py: Python) -> PyResult> { - let shape = PyList::empty(py); + let shape = PyList::empty_bound(py); if let Some(d) = &self.0.shape { for dim in d.dim.iter() { if let Some(value) = &dim.value { diff --git a/candle-pyo3/stub.py b/candle-pyo3/stub.py index 165941bd..b0e472e6 100644 --- a/candle-pyo3/stub.py +++ b/candle-pyo3/stub.py @@ -206,6 +206,8 @@ def write(module, directory, origin, check=False): if check: with open(filename, "r") as f: data = f.read() + print("generated content") + print(pyi_content) assert data == pyi_content, f"The content of {filename} seems outdated, please run `python stub.py`" else: with open(filename, "w") as f: @@ -229,6 +231,8 @@ def write(module, directory, origin, check=False): if check: with open(filename, "r") as f: data = f.read() + print("generated content") + print(py_content) assert data == py_content, f"The content of {filename} seems outdated, please run `python stub.py`" else: with open(filename, "w") as f: