use eredu_nn::{
DistributedNeuralBackend, GroupSelection, GroupedGatedProductOperator, GroupedNeuralBackend,
GroupedRelu2Operator, Tensor, TensorParallelGroupedOutput,
};
use crate::ExpertPass;
use crate::{ActivationObserver, ParameterBankKey, RoutingObservation};
pub trait AddressableGatedProductBank<B>
where
B: GroupedNeuralBackend,
{
type Error;
fn acquire(
&mut self,
key: ParameterBankKey,
spec: &eredu_nn::GroupedGatedProductSpec,
context: &<B::Tensor as Tensor>::Context,
) -> Result<&mut B::GatedProductGroups, Self::Error>;
}
pub struct RoutedExpertRequest<'a, T> {
pub layer: usize,
pub input: &'a T,
pub routes: &'a GroupSelection<T>,
pub pass: ExpertPass,
}
pub enum RoutedExpertTensorParallelOutput<T> {
Complete(T),
Partial(TensorParallelGroupedOutput<T>),
}
pub fn reduce_tensor_parallel_expert_output<B>(
output: TensorParallelGroupedOutput<B::Tensor>,
parallel: &B::ParallelContext,
context: &<B::Tensor as Tensor>::Context,
) -> Result<B::Tensor, eredu_nn::Error>
where
B: GroupedNeuralBackend + DistributedNeuralBackend,
{
let reduced = B::sum_parallel(output.reducible().clone(), parallel, context)?;
match output.post_reduce().cloned() {
Some(bias) => reduced.add(&bias, context),
None => Ok(reduced),
}
}
pub fn combine_tensor_parallel_expert_outputs<B>(
left: TensorParallelGroupedOutput<B::Tensor>,
right: TensorParallelGroupedOutput<B::Tensor>,
context: &<B::Tensor as Tensor>::Context,
) -> Result<TensorParallelGroupedOutput<B::Tensor>, eredu_nn::Error>
where
B: GroupedNeuralBackend,
{
let post_reduce = match (left.post_reduce().cloned(), right.post_reduce().cloned()) {
(Some(left), Some(right)) => Some(left.add(&right, context)?),
(Some(bias), None) | (None, Some(bias)) => Some(bias),
(None, None) => None,
};
Ok(TensorParallelGroupedOutput::new(
left.reducible().add(right.reducible(), context)?,
post_reduce,
))
}
pub fn combine_routed_expert_tensor_parallel<B>(
left: RoutedExpertTensorParallelOutput<B::Tensor>,
right: RoutedExpertTensorParallelOutput<B::Tensor>,
context: &<B::Tensor as Tensor>::Context,
) -> Result<RoutedExpertTensorParallelOutput<B::Tensor>, eredu_nn::Error>
where
B: GroupedNeuralBackend,
{
match (left, right) {
(
RoutedExpertTensorParallelOutput::Complete(left),
RoutedExpertTensorParallelOutput::Complete(right),
) => Ok(RoutedExpertTensorParallelOutput::Complete(
left.add(&right, context)?,
)),
(
RoutedExpertTensorParallelOutput::Partial(left),
RoutedExpertTensorParallelOutput::Partial(right),
) => combine_tensor_parallel_expert_outputs::<B>(left, right, context)
.map(RoutedExpertTensorParallelOutput::Partial),
_ => Err(eredu_nn::Error::backend(
"provider mixed complete and rank-local expert outputs in one block",
)),
}
}
pub fn reduce_routed_expert_tensor_parallel<B>(
output: RoutedExpertTensorParallelOutput<B::Tensor>,
parallel: &B::ParallelContext,
context: &<B::Tensor as Tensor>::Context,
) -> Result<B::Tensor, eredu_nn::Error>
where
B: GroupedNeuralBackend + DistributedNeuralBackend,
{
match output {
RoutedExpertTensorParallelOutput::Complete(output) => Ok(output),
RoutedExpertTensorParallelOutput::Partial(output) => {
reduce_tensor_parallel_expert_output::<B>(output, parallel, context)
}
}
}
pub trait RoutedExpertProvider<B>
where
B: GroupedNeuralBackend,
{
type Error;
fn forward_grouped(
&mut self,
resident_bank: &mut B::GatedProductGroups,
request: RoutedExpertRequest<'_, B::Tensor>,
context: &<B::Tensor as Tensor>::Context,
) -> Result<B::Tensor, Self::Error>;
fn forward_relu2_routed(
&mut self,
resident_bank: &mut B::Relu2Groups,
request: RoutedExpertRequest<'_, B::Tensor>,
context: &<B::Tensor as Tensor>::Context,
) -> Result<B::Tensor, Self::Error>;
}
pub trait TensorParallelRoutedExpertProvider<B>: RoutedExpertProvider<B>
where
B: GroupedNeuralBackend,
{
fn forward_grouped_tensor_parallel(
&mut self,
resident_bank: &mut B::GatedProductGroups,
request: RoutedExpertRequest<'_, B::Tensor>,
partitions: usize,
context: &<B::Tensor as Tensor>::Context,
) -> Result<RoutedExpertTensorParallelOutput<B::Tensor>, Self::Error>;
fn forward_relu2_routed_tensor_parallel(
&mut self,
resident_bank: &mut B::Relu2Groups,
request: RoutedExpertRequest<'_, B::Tensor>,
partitions: usize,
context: &<B::Tensor as Tensor>::Context,
) -> Result<RoutedExpertTensorParallelOutput<B::Tensor>, Self::Error>;
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RoutedObservationPoint {
path: String,
expert_count: i32,
}
impl RoutedObservationPoint {
pub fn new(path: impl Into<String>, expert_count: i32) -> Self {
Self {
path: path.into(),
expert_count,
}
}
pub fn path(&self) -> &str {
&self.path
}
pub const fn expert_count(&self) -> i32 {
self.expert_count
}
}
#[derive(Debug)]
pub enum ObservedExpertProviderError<P, O> {
Provider(P),
Observer(O),
}
impl<P, O> std::fmt::Display for ObservedExpertProviderError<P, O>
where
P: std::fmt::Display,
O: std::fmt::Display,
{
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Provider(error) => write!(formatter, "routed expert provider failed: {error}"),
Self::Observer(error) => write!(formatter, "routed expert observer failed: {error}"),
}
}
}
impl<P, O> std::error::Error for ObservedExpertProviderError<P, O>
where
P: std::error::Error + 'static,
O: std::error::Error + 'static,
{
}
pub struct ObservedExpertProvider<'a, P, O: ?Sized, E> {
provider: &'a mut P,
observer: &'a mut O,
point: RoutedObservationPoint,
error: std::marker::PhantomData<fn() -> E>,
}
impl<'a, P, O: ?Sized, E> ObservedExpertProvider<'a, P, O, E> {
pub fn new(provider: &'a mut P, observer: &'a mut O, point: RoutedObservationPoint) -> Self {
Self {
provider,
observer,
point,
error: std::marker::PhantomData,
}
}
fn observe<T, ObservationError>(
&mut self,
routes: &eredu_nn::GroupSelection<T>,
output: &T,
) -> Result<(), ObservationError>
where
O: ActivationObserver<T, ObservationError>,
{
self.observer.observe_routing(RoutingObservation {
path: self.point.path(),
selected_experts: routes.group_indices(),
selected_scores: routes.selected_scores(),
coefficients: routes.coefficients(),
routed_output: output,
local_routed_output: None,
reduced_routed_output: None,
shared_output: None,
combined_output: None,
expert_count: self.point.expert_count(),
})
}
}
impl<B, P, O, E> RoutedExpertProvider<B> for ObservedExpertProvider<'_, P, O, E>
where
B: GroupedNeuralBackend,
P: RoutedExpertProvider<B>,
O: ActivationObserver<B::Tensor, E> + ?Sized,
{
type Error = ObservedExpertProviderError<P::Error, E>;
fn forward_grouped(
&mut self,
resident_bank: &mut B::GatedProductGroups,
request: RoutedExpertRequest<'_, B::Tensor>,
context: &<B::Tensor as Tensor>::Context,
) -> Result<B::Tensor, Self::Error> {
let routes = request.routes;
let output = self
.provider
.forward_grouped(resident_bank, request, context)
.map_err(ObservedExpertProviderError::Provider)?;
self.observe(routes, &output)
.map_err(ObservedExpertProviderError::Observer)?;
Ok(output)
}
fn forward_relu2_routed(
&mut self,
resident_bank: &mut B::Relu2Groups,
request: RoutedExpertRequest<'_, B::Tensor>,
context: &<B::Tensor as Tensor>::Context,
) -> Result<B::Tensor, Self::Error> {
let routes = request.routes;
let output = self
.provider
.forward_relu2_routed(resident_bank, request, context)
.map_err(ObservedExpertProviderError::Provider)?;
self.observe(routes, &output)
.map_err(ObservedExpertProviderError::Observer)?;
Ok(output)
}
}
impl<B, P, O, E> TensorParallelRoutedExpertProvider<B> for ObservedExpertProvider<'_, P, O, E>
where
B: GroupedNeuralBackend,
P: TensorParallelRoutedExpertProvider<B>,
O: ActivationObserver<B::Tensor, E> + ?Sized,
{
fn forward_grouped_tensor_parallel(
&mut self,
resident_bank: &mut B::GatedProductGroups,
request: RoutedExpertRequest<'_, B::Tensor>,
partitions: usize,
context: &<B::Tensor as Tensor>::Context,
) -> Result<RoutedExpertTensorParallelOutput<B::Tensor>, Self::Error> {
self.provider
.forward_grouped_tensor_parallel(resident_bank, request, partitions, context)
.map_err(ObservedExpertProviderError::Provider)
}
fn forward_relu2_routed_tensor_parallel(
&mut self,
resident_bank: &mut B::Relu2Groups,
request: RoutedExpertRequest<'_, B::Tensor>,
partitions: usize,
context: &<B::Tensor as Tensor>::Context,
) -> Result<RoutedExpertTensorParallelOutput<B::Tensor>, Self::Error> {
self.provider
.forward_relu2_routed_tensor_parallel(resident_bank, request, partitions, context)
.map_err(ObservedExpertProviderError::Provider)
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct ResidentExpertProvider;
impl<B> RoutedExpertProvider<B> for ResidentExpertProvider
where
B: GroupedNeuralBackend,
{
type Error = eredu_nn::Error;
fn forward_grouped(
&mut self,
resident_bank: &mut B::GatedProductGroups,
request: RoutedExpertRequest<'_, B::Tensor>,
context: &<B::Tensor as Tensor>::Context,
) -> Result<B::Tensor, Self::Error> {
resident_bank.forward_grouped(request.input, request.routes, context)
}
fn forward_relu2_routed(
&mut self,
resident_bank: &mut B::Relu2Groups,
request: RoutedExpertRequest<'_, B::Tensor>,
context: &<B::Tensor as Tensor>::Context,
) -> Result<B::Tensor, Self::Error> {
resident_bank.forward_grouped(request.input, request.routes, context)
}
}
impl<B> TensorParallelRoutedExpertProvider<B> for ResidentExpertProvider
where
B: eredu_nn::TensorParallelGroupedNeuralBackend,
{
fn forward_grouped_tensor_parallel(
&mut self,
resident_bank: &mut B::GatedProductGroups,
request: RoutedExpertRequest<'_, B::Tensor>,
partitions: usize,
context: &<B::Tensor as Tensor>::Context,
) -> Result<RoutedExpertTensorParallelOutput<B::Tensor>, Self::Error> {
B::gated_product_groups_tensor_parallel(
resident_bank,
request.input,
request.routes,
partitions,
context,
)
.map(RoutedExpertTensorParallelOutput::Partial)
}
fn forward_relu2_routed_tensor_parallel(
&mut self,
resident_bank: &mut B::Relu2Groups,
request: RoutedExpertRequest<'_, B::Tensor>,
partitions: usize,
context: &<B::Tensor as Tensor>::Context,
) -> Result<RoutedExpertTensorParallelOutput<B::Tensor>, Self::Error> {
B::relu2_groups_tensor_parallel(
resident_bank,
request.input,
request.routes,
partitions,
context,
)
.map(RoutedExpertTensorParallelOutput::Partial)
}
}