fuel_tensor/lib.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! `fuel-tensor` — Fuel's user-facing `Tensor` handle and `Device`
3//! abstraction, moved here from `fuel-core` (board #109; CireSnave:
4//! "Move Tensor into fuel-tensor. We can worry about a semantic split
5//! later."). This is a MECHANICAL move -- every item below kept its own
6//! content; only the crate boundary changed. `fuel-core`'s own
7//! `lib.rs` re-exports these same paths so existing
8//! `fuel_tensor::lazy::Tensor` / `fuel::Tensor` call sites keep compiling.
9//!
10//! The moved cluster (`lazy`, `device`, the 3 GPU backend bridges,
11//! `dtype`'s `WithDType`/`IntDType`/`FloatDType` trio, `pipelined_bridge`,
12//! `judge`, `factories`, `planner`, `decode_shape`, `lazy_latent_cache`,
13//! `test_utils`, `scheduling`, `inference_context`,
14//! `kv_block_pool_device`, `persistent_decode`, `nf4`) moved together in
15//! one PR because it is one mutually-referencing graph, not several
16//! independent files that happen to touch `Tensor` -- measured via a
17//! full crate::-reference census before any file moved, not assumed
18//! from file names. See `docs/release-0.13.0-wave.md`.
19//!
20//! ```rust
21//! use fuel_tensor::lazy::{Tensor, realize_many_f32};
22//! use fuel_tensor::Device;
23//! # use fuel_tensor::Error;
24//! # fn main() -> Result<(), Error> {
25//! let dev = Device::cpu();
26//! let a = Tensor::from_f32((0..6).map(|x| x as f32).collect::<Vec<_>>(), (2, 3), &dev)?;
27//! let b = Tensor::from_f32_on(a.graph(), (0..12).map(|x| x as f32).collect::<Vec<_>>(), (3, 4), &dev)?;
28//! let c = a.matmul(&b)?;
29//! assert_eq!(c.shape().dims(), &[2, 4]);
30//! let out = realize_many_f32(&[&c]);
31//! assert_eq!(out[0].len(), 8);
32//! # Ok(())}
33//! ```
34
35// GAP-229: `clippy::identity_op` fires across fuel-core+fuel-dispatch (and now
36// fuel-tensor, since the flagged sites in judge/mod.rs and lazy.rs moved here)
37// and is a defect in 0 of them — it measures a house idiom, not debt, so it is
38// allowed at the crate root. Carried forward verbatim from fuel-core/src/lib.rs
39// (board #109 move) rather than re-derived, since the measurement and the two
40// intentional classes it documents (DOC-INDEX, DOC-SHAPE) are about the CODE
41// that moved, not about which crate currently compiles it. See fuel-core's own
42// copy of this comment (unaffected by this move) for the full rationale.
43#![allow(clippy::identity_op)]
44
45pub mod cuda_backend;
46pub mod decode_shape;
47pub mod device;
48pub mod dtype;
49pub mod factories;
50pub mod inference_context;
51pub mod judge;
52pub mod kv_block_pool_device;
53pub mod lazy;
54pub mod lazy_latent_cache;
55pub mod metal_backend;
56pub mod nf4;
57pub mod persistent_decode;
58pub mod pipelined_bridge;
59pub mod planner;
60pub mod reproducibility;
61pub mod scheduling;
62pub mod test_utils;
63#[cfg(feature = "vulkan")]
64pub mod vulkan_backend;
65
66pub use device::{Device, DeviceLocation, NdArray};
67pub use dtype::{DType, DTypeParseError, FloatDType, IntDType, WithDType};
68pub use fuel_ir::error::{Context, Error, Result};
69// `device.rs` uses `crate::Layout`/`crate::Shape` (root-qualified) internally;
70// these are the same fuel_ir re-exports fuel-core's own root carries, needed
71// here because fuel-tensor is now the crate device.rs's `crate::` resolves
72// within.
73pub use fuel_ir::layout::Layout;
74pub use fuel_ir::shape::{D, Shape};
75// device.rs's `use crate::{DType, HostBuffer, Result, Shape, Storage,
76// WithDType};` -- a GROUPED import, which an earlier `crate::X` text census
77// missed (it doesn't contain the substring `crate::HostBuffer`, only
78// `HostBuffer` inside `crate::{...}`'s braces). Caught by the first real
79// `cargo check`, not the census.
80pub use fuel_backend_contract::Storage;
81pub use fuel_ir::HostBuffer;
82
83#[doc(hidden)]
84#[cfg(feature = "cuda")]
85pub use cuda_backend as cuda;
86
87#[cfg(feature = "cuda")]
88pub use cuda_backend::{CudaDevice, CudaStorage};
89
90#[cfg(feature = "cuda")]
91pub use fuel_cuda_backend::builder_arg;
92
93#[cfg(feature = "metal")]
94pub use metal_backend::{MetalDevice, MetalError, MetalStorage};
95
96#[cfg(feature = "mkl")]
97extern crate intel_mkl_src;
98
99#[cfg(feature = "accelerate")]
100extern crate accelerate_src;
101
102// `bail!` consolidation (fuel-core dissolution, GAP-347 PR 4): this used to
103// be a LOCAL duplicate of `fuel_core::bail!`, kept separate only because
104// depending on `fuel_core::bail!` directly would have created a
105// `fuel-tensor` <-> `fuel-core` cargo cycle (`fuel-core` needs
106// `fuel-tensor` for `train.rs`). That reasoning never applied to
107// `fuel_ir`: this crate already depends on it directly (`Error`/`Result`
108// above are a bare re-export of `fuel_ir::error::{Error, Result}`, not a
109// distinct type), and `fuel_ir` is tier 10 — below everything. `$crate`
110// inside a `macro_rules!` body resolves to the crate that WROTE the
111// macro, not the one that re-exports or invokes it, so re-exporting here
112// keeps `fuel_tensor::bail!` producing byte-identical `fuel_ir::Error::Msg`
113// values — proved by the pre-existing `bail_tests::bail_macro_returns_a_typed_err`
114// below, which is unchanged and still exercises this re-export.
115pub use fuel_ir::bail;
116
117#[cfg(test)]
118mod bail_tests {
119 use crate::bail;
120 use fuel_ir::error::Result;
121
122 fn always_bails() -> Result<()> {
123 bail!("deliberate failure for the macro test");
124 #[allow(unreachable_code)]
125 Ok(())
126 }
127
128 #[test]
129 fn bail_macro_returns_a_typed_err() {
130 let err = always_bails().expect_err("bail! must produce an Err, never panic");
131 assert!(err.to_string().contains("deliberate failure"));
132 }
133}