use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Modality {
Image,
Text,
Audio,
}
impl fmt::Display for Modality {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Modality::Image => write!(f, "image"),
Modality::Text => write!(f, "text"),
Modality::Audio => write!(f, "audio"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ActivationKind {
Relu,
Gelu,
Tanh,
Sigmoid,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MultimodalLayer {
ImageConv {
channels: u32,
kernel: u32,
},
ImagePool {
kernel: u32,
},
TextEmbed {
dim: u32,
},
TextTransformer {
heads: u32,
dim: u32,
},
AudioConv {
channels: u32,
kernel: u32,
},
AudioRecurrent {
hidden: u32,
},
Dense {
out: u32,
},
Norm,
Activation {
kind: ActivationKind,
},
}
pub fn layer_modality(layer: &MultimodalLayer) -> Option<Modality> {
match layer {
MultimodalLayer::ImageConv { .. } | MultimodalLayer::ImagePool { .. } => {
Some(Modality::Image)
}
MultimodalLayer::TextEmbed { .. } | MultimodalLayer::TextTransformer { .. } => {
Some(Modality::Text)
}
MultimodalLayer::AudioConv { .. } | MultimodalLayer::AudioRecurrent { .. } => {
Some(Modality::Audio)
}
MultimodalLayer::Dense { .. }
| MultimodalLayer::Norm
| MultimodalLayer::Activation { .. } => None,
}
}
pub fn layer_capacity(layer: &MultimodalLayer) -> f64 {
match layer {
MultimodalLayer::ImageConv { channels, .. } => f64::from(*channels),
MultimodalLayer::ImagePool { .. } => 8.0,
MultimodalLayer::TextEmbed { dim } => f64::from(*dim),
MultimodalLayer::TextTransformer { heads, dim } => f64::from(*heads) * f64::from(*dim),
MultimodalLayer::AudioConv { channels, .. } => f64::from(*channels),
MultimodalLayer::AudioRecurrent { hidden } => f64::from(*hidden),
MultimodalLayer::Dense { out } => f64::from(*out),
MultimodalLayer::Norm => 4.0,
MultimodalLayer::Activation { .. } => 1.0,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum FusionOp {
EarlyConcat,
LateFusion,
CrossAttention {
heads: u32,
dim: u32,
},
Gated {
dim: u32,
},
Bilinear {
dim: u32,
},
}
pub fn fusion_min_modalities(fusion: &FusionOp) -> usize {
match fusion {
FusionOp::EarlyConcat | FusionOp::LateFusion => 1,
FusionOp::CrossAttention { .. } | FusionOp::Gated { .. } | FusionOp::Bilinear { .. } => 2,
}
}
pub fn fusion_max_modalities(fusion: &FusionOp) -> Option<usize> {
match fusion {
FusionOp::Bilinear { .. } => Some(2),
_ => None,
}
}
pub fn fusion_supports(fusion: &FusionOp, num_modalities: usize) -> bool {
let min_m = fusion_min_modalities(fusion);
match fusion_max_modalities(fusion) {
None => num_modalities >= min_m,
Some(max_m) => num_modalities >= min_m && num_modalities <= max_m,
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ModalityEncoder {
pub modality: Modality,
pub layers: Vec<MultimodalLayer>,
}
pub fn encoder_capacity(encoder: &ModalityEncoder) -> f64 {
encoder.layers.iter().map(layer_capacity).sum()
}