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