1use rlx_ir::{DType, Graph, GraphExt, NodeId, Op, Shape};
22
23use crate::Tensor;
24use crate::handle::GraphHandle;
25
26pub fn cat(tensors: &[&Tensor], axis: usize) -> Tensor {
29 assert!(!tensors.is_empty(), "cat: need at least one tensor");
30 let root = tensors[0];
31 let ids: Vec<NodeId> = tensors.iter().map(|t| root.adopt(t)).collect();
32 let id = root.handle.with_graph(|g| g.concat_(ids, axis));
33 Tensor::new(root.handle.clone(), id)
34}
35
36pub fn stack(tensors: &[&Tensor], axis: usize) -> Tensor {
39 assert!(!tensors.is_empty(), "stack: need at least one tensor");
40 let expanded: Vec<Tensor> = tensors.iter().map(|t| t.unsqueeze(axis)).collect();
41 let refs: Vec<&Tensor> = expanded.iter().collect();
42 cat(&refs, axis)
43}
44
45impl Tensor {
46 pub fn from_vec(data: impl Into<Vec<f32>>, shape: impl AsRef<[usize]>) -> Self {
49 let data = data.into();
50 let dims = shape.as_ref();
51 let shape = Shape::new(dims, DType::F32);
52 let n = shape
53 .num_elements()
54 .expect("from_vec: shape must be static");
55 assert_eq!(
56 n,
57 data.len(),
58 "from_vec: shape {dims:?} expects {n} elements, got {}",
59 data.len()
60 );
61 Self::constant_f32(&data, shape)
62 }
63
64 pub fn zeros(shape: impl AsRef<[usize]>) -> Self {
66 Self::full(shape, 0.0)
67 }
68
69 pub fn ones(shape: impl AsRef<[usize]>) -> Self {
71 Self::full(shape, 1.0)
72 }
73
74 pub fn full(shape: impl AsRef<[usize]>, value: f32) -> Self {
76 let dims = shape.as_ref();
77 let shape = Shape::new(dims, DType::F32);
78 let n = shape.num_elements().expect("full: shape must be static");
79 Self::constant_f32(&vec![value; n], shape)
80 }
81
82 pub fn eye(n: usize) -> Self {
84 let mut data = vec![0.0f32; n * n];
85 for i in 0..n {
86 data[i * n + i] = 1.0;
87 }
88 Self::constant_f32(&data, Shape::new(&[n, n], DType::F32))
89 }
90
91 pub fn arange(end: i64) -> Self {
93 Self::arange_step(0, end, 1)
94 }
95
96 pub fn arange_step(start: i64, end: i64, step: i64) -> Self {
98 assert!(step != 0, "arange: step must be non-zero");
99 let mut v = Vec::new();
100 let mut x = start;
101 if step > 0 {
102 while x < end {
103 v.push(x as f32);
104 x += step;
105 }
106 } else {
107 while x > end {
108 v.push(x as f32);
109 x += step;
110 }
111 }
112 let len = v.len();
113 Self::constant_f32(&v, Shape::new(&[len], DType::F32))
114 }
115
116 pub fn from_f64(data: impl AsRef<[f64]>, shape: impl AsRef<[usize]>) -> Self {
118 let data = data.as_ref();
119 let shape = Shape::new(shape.as_ref(), DType::F64);
120 let n = shape
121 .num_elements()
122 .expect("from_f64: shape must be static");
123 assert_eq!(
124 n,
125 data.len(),
126 "from_f64: shape expects {n} elements, got {}",
127 data.len()
128 );
129 let bytes = data.iter().flat_map(|v| v.to_le_bytes()).collect();
130 Self::constant_raw(bytes, shape)
131 }
132
133 pub fn from_i64(data: impl AsRef<[i64]>, shape: impl AsRef<[usize]>) -> Self {
135 let data = data.as_ref();
136 let shape = Shape::new(shape.as_ref(), DType::I64);
137 let n = shape
138 .num_elements()
139 .expect("from_i64: shape must be static");
140 assert_eq!(
141 n,
142 data.len(),
143 "from_i64: shape expects {n} elements, got {}",
144 data.len()
145 );
146 let bytes = data.iter().flat_map(|v| v.to_le_bytes()).collect();
147 Self::constant_raw(bytes, shape)
148 }
149
150 pub fn index_vec(data: impl AsRef<[i64]>) -> Self {
153 let data = data.as_ref();
154 Self::from_i64(data, [data.len()])
155 }
156
157 fn constant_f32(data: &[f32], shape: Shape) -> Self {
159 let bytes = data.iter().flat_map(|x| x.to_le_bytes()).collect();
160 Self::constant_raw(bytes, shape)
161 }
162
163 pub fn rand(shape: impl AsRef<[usize]>, seed: u64) -> Self {
165 Self::rand_range(shape, seed, 0.0, 1.0)
166 }
167
168 pub fn rand_range(shape: impl AsRef<[usize]>, seed: u64, lo: f32, hi: f32) -> Self {
170 let shape = Shape::new(shape.as_ref(), DType::F32);
171 let n = shape
172 .num_elements()
173 .expect("rand_range: shape must be static");
174 let mut s = seed_state(seed);
175 let data: Vec<f32> = (0..n).map(|_| lo + (hi - lo) * next_unit(&mut s)).collect();
176 Self::constant_f32(&data, shape)
177 }
178
179 pub fn randn(shape: impl AsRef<[usize]>, seed: u64) -> Self {
181 Self::randn_std(shape, seed, 0.0, 1.0)
182 }
183
184 pub fn randn_std(shape: impl AsRef<[usize]>, seed: u64, mean: f32, std: f32) -> Self {
187 let shape = Shape::new(shape.as_ref(), DType::F32);
188 let n = shape
189 .num_elements()
190 .expect("randn_std: shape must be static");
191 let mut s = seed_state(seed);
192 let data: Vec<f32> = (0..n).map(|_| mean + std * next_normal(&mut s)).collect();
193 Self::constant_f32(&data, shape)
194 }
195
196 fn constant_raw(bytes: Vec<u8>, shape: Shape) -> Self {
198 let mut graph = Graph::new("rlx");
199 let id = graph.add_node(Op::Constant { data: bytes }, vec![], shape);
200 Tensor::new(GraphHandle::new(graph), id)
201 }
202}
203
204fn seed_state(seed: u64) -> u64 {
207 seed ^ 0x9E37_79B9_7F4A_7C15
209}
210
211fn next_u64(s: &mut u64) -> u64 {
212 *s = s.wrapping_add(0x9E37_79B9_7F4A_7C15);
213 let mut z = *s;
214 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
215 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
216 z ^ (z >> 31)
217}
218
219fn next_unit(s: &mut u64) -> f32 {
221 (next_u64(s) >> 40) as f32 / (1u64 << 24) as f32
222}
223
224fn next_normal(s: &mut u64) -> f32 {
226 let u1 = next_unit(s).max(f32::MIN_POSITIVE);
227 let u2 = next_unit(s);
228 (-2.0 * u1.ln()).sqrt() * (std::f32::consts::TAU * u2).cos()
229}