1use nuvec::distributed::{self, Group};
29use nuvec::{Array, Error, Param, Result};
30
31use crate::{Linear, Module, QuantizedLinear};
32
33#[derive(Debug, Clone, Module)]
36pub enum Shard {
37 Linear(Linear),
39 Quantized(QuantizedLinear),
41}
42
43impl Shard {
44 #[track_caller]
45 fn forward(&self, x: &Array) -> Array {
46 match self {
47 Self::Linear(l) => l.forward(x),
48 Self::Quantized(q) => q.forward(x),
49 }
50 }
51}
52
53fn slice(layer: &str, what: &str, n: i32, unit: i32, group: &Group) -> Result<(i32, i32)> {
55 let size = group.size() as i32;
56 if n % (size * unit) != 0 {
57 return Err(Error::InvalidArgument(format!(
58 "{layer}: {n} {what} do not split into {size} shards{}",
59 if unit > 1 {
60 format!(" of whole groups of {unit}")
61 } else {
62 String::new()
63 }
64 )));
65 }
66 let per = n / size;
67 let r = group.rank() as i32;
68 Ok((r * per, (r + 1) * per))
69}
70
71fn dims(a: &Array) -> (i32, i32) {
72 let s = a.shape();
73 (
74 s.first().copied().unwrap_or(0),
75 s.get(1).copied().unwrap_or(0),
76 )
77}
78
79#[derive(Debug, Clone, Module)]
84pub struct AllToShardedLinear {
85 pub shard: Shard,
87 #[tree(skip)]
89 pub group: Group,
90}
91
92impl AllToShardedLinear {
93 pub fn from_linear(linear: &Linear, group: &Group) -> Result<Self> {
96 let (out, _) = dims(&linear.weight);
97 let (a, b) = slice("AllToShardedLinear", "output features", out, 1, group)?;
98 Ok(Self {
99 shard: Shard::Linear(Linear {
100 weight: Param::new(linear.weight.i((a..b, ..))),
101 bias: linear.bias.as_ref().map(|p| Param::new(p.i(a..b))),
102 }),
103 group: group.clone(),
104 })
105 }
106
107 pub fn from_quantized(layer: &QuantizedLinear, group: &Group) -> Result<Self> {
109 let (out, _) = dims(&layer.weight);
110 let (a, b) = slice("AllToShardedLinear", "output features", out, 1, group)?;
111 let rows = |x: &Array| x.i((a..b, ..));
112 Ok(Self {
113 shard: Shard::Quantized(QuantizedLinear {
114 weight: rows(&layer.weight),
115 scales: rows(&layer.scales),
116 biases: layer.biases.as_ref().map(rows),
117 bias: layer.bias.as_ref().map(|p| Param::new(p.i(a..b))),
118 quant: layer.quant,
119 }),
120 group: group.clone(),
121 })
122 }
123
124 #[track_caller]
126 pub fn forward(&self, x: &Array) -> Array {
127 self.shard
128 .forward(&distributed::sum_gradients(x, Some(&self.group)))
129 }
130}
131
132#[derive(Debug, Clone, Module)]
137pub struct ShardedToAllLinear {
138 pub shard: Shard,
140 pub bias: Option<Param>,
142 #[tree(skip)]
144 pub group: Group,
145}
146
147impl ShardedToAllLinear {
148 pub fn from_linear(linear: &Linear, group: &Group) -> Result<Self> {
151 let (_, input) = dims(&linear.weight);
152 let (a, b) = slice("ShardedToAllLinear", "input features", input, 1, group)?;
153 Ok(Self {
154 shard: Shard::Linear(Linear {
155 weight: Param::new(linear.weight.i((.., a..b))),
156 bias: None,
157 }),
158 bias: linear.bias.clone(),
159 group: group.clone(),
160 })
161 }
162
163 pub fn from_quantized(layer: &QuantizedLinear, group: &Group) -> Result<Self> {
166 let (_, packed) = dims(&layer.weight);
167 let bits = layer.quant.bits.max(1);
168 let input = packed * 32 / bits;
169 let group_size = layer.quant.group_size.max(1);
170 let (a, b) = slice(
171 "ShardedToAllLinear",
172 "input features",
173 input,
174 group_size,
175 group,
176 )?;
177 let (pa, pb) = (a * bits / 32, b * bits / 32);
178 let (sa, sb) = (a / group_size, b / group_size);
179 let cols = |x: &Array| x.i((.., sa..sb));
180 Ok(Self {
181 shard: Shard::Quantized(QuantizedLinear {
182 weight: layer.weight.i((.., pa..pb)),
183 scales: cols(&layer.scales),
184 biases: layer.biases.as_ref().map(cols),
185 bias: None,
186 quant: layer.quant,
187 }),
188 bias: layer.bias.clone(),
189 group: group.clone(),
190 })
191 }
192
193 #[track_caller]
195 pub fn forward(&self, x: &Array) -> Array {
196 let y = distributed::all_sum(&self.shard.forward(x), Some(&self.group));
197 match &self.bias {
198 Some(b) => y.add(b),
199 None => y,
200 }
201 }
202}