use std::hash::Hash;
use super::{PdaTransition, StackSymbol};
use crate::semiring::Semiring;
use crate::wfst::StateId;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub enum PdaAcceptMode {
#[default]
FinalState,
EmptyStack,
Both,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PdaConfiguration<L> {
pub state: StateId,
pub remaining_input: Vec<L>,
pub stack: Vec<StackSymbol>,
}
impl<L> PdaConfiguration<L> {
pub fn new(state: StateId, remaining_input: Vec<L>, stack: Vec<StackSymbol>) -> Self {
Self {
state,
remaining_input,
stack,
}
}
pub fn initial(start: StateId, input: Vec<L>, initial_stack: StackSymbol) -> Self {
Self {
state: start,
remaining_input: input,
stack: vec![initial_stack],
}
}
pub fn input_exhausted(&self) -> bool {
self.remaining_input.is_empty()
}
pub fn stack_empty(&self) -> bool {
self.stack.is_empty()
}
pub fn stack_top(&self) -> Option<StackSymbol> {
self.stack.last().copied()
}
pub fn next_input(&self) -> Option<&L> {
self.remaining_input.first()
}
}
impl<L: Clone> PdaConfiguration<L> {
pub fn apply_transition(&self, transition: &PdaTransition<L, impl Semiring>) -> Option<Self>
where
L: PartialEq,
{
let stack_top = self.stack_top()?;
if stack_top != transition.stack_top {
return None;
}
let mut new_input = self.remaining_input.clone();
match &transition.input {
Some(expected) => {
if self.next_input() != Some(expected) {
return None;
}
new_input.remove(0); }
None => {
}
}
let mut new_stack = self.stack.clone();
if !transition.stack_action.apply(&mut new_stack) {
return None;
}
Some(Self {
state: transition.to,
remaining_input: new_input,
stack: new_stack,
})
}
}
pub trait WeightedPda<L, W: Semiring>: Clone + Send + Sync {
fn start(&self) -> StateId;
fn initial_stack(&self) -> StackSymbol;
fn is_final(&self, state: StateId) -> bool;
fn final_weight(&self, state: StateId) -> W;
fn accept_mode(&self) -> PdaAcceptMode;
fn accepts_empty_stack(&self) -> bool {
matches!(
self.accept_mode(),
PdaAcceptMode::EmptyStack | PdaAcceptMode::Both
)
}
fn accepts_final_state(&self) -> bool {
matches!(
self.accept_mode(),
PdaAcceptMode::FinalState | PdaAcceptMode::Both
)
}
fn transitions(&self, state: StateId) -> &[PdaTransition<L, W>];
fn matching_transitions(
&self,
state: StateId,
input: Option<&L>,
stack_top: StackSymbol,
) -> Vec<&PdaTransition<L, W>>
where
L: PartialEq,
{
self.transitions(state)
.iter()
.filter(|t| t.matches(input, stack_top))
.collect()
}
fn epsilon_transitions(
&self,
state: StateId,
stack_top: StackSymbol,
) -> Vec<&PdaTransition<L, W>>
where
L: PartialEq,
{
self.transitions(state)
.iter()
.filter(|t| t.is_epsilon() && t.stack_top == stack_top)
.collect()
}
fn num_states(&self) -> usize;
fn num_transitions(&self) -> usize;
fn states(&self) -> impl Iterator<Item = StateId>;
fn final_states(&self) -> impl Iterator<Item = StateId>;
fn is_empty(&self) -> bool {
self.num_states() == 0
}
fn is_accepting(&self, config: &PdaConfiguration<L>) -> bool {
if !config.input_exhausted() {
return false;
}
match self.accept_mode() {
PdaAcceptMode::FinalState => self.is_final(config.state),
PdaAcceptMode::EmptyStack => config.stack_empty(),
PdaAcceptMode::Both => self.is_final(config.state) || config.stack_empty(),
}
}
fn accepting_weight(&self, config: &PdaConfiguration<L>) -> Option<W>
where
W: Clone,
{
if !config.input_exhausted() {
return None;
}
match self.accept_mode() {
PdaAcceptMode::FinalState => {
if self.is_final(config.state) {
Some(self.final_weight(config.state))
} else {
None
}
}
PdaAcceptMode::EmptyStack => {
if config.stack_empty() {
Some(W::one())
} else {
None
}
}
PdaAcceptMode::Both => {
if self.is_final(config.state) {
Some(self.final_weight(config.state))
} else if config.stack_empty() {
Some(W::one())
} else {
None
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pushdown::stack::StackAction;
use crate::semiring::TropicalWeight;
#[test]
fn test_accept_mode_default() {
assert_eq!(PdaAcceptMode::default(), PdaAcceptMode::FinalState);
}
#[test]
fn test_configuration_initial() {
let config: PdaConfiguration<char> =
PdaConfiguration::initial(0, vec!['a', 'b', 'c'], StackSymbol::BOTTOM);
assert_eq!(config.state, 0);
assert_eq!(config.remaining_input, vec!['a', 'b', 'c']);
assert_eq!(config.stack, vec![StackSymbol::BOTTOM]);
}
#[test]
fn test_configuration_stack_top() {
let config: PdaConfiguration<char> =
PdaConfiguration::new(0, vec![], vec![StackSymbol::BOTTOM, StackSymbol::new(1)]);
assert_eq!(config.stack_top(), Some(StackSymbol::new(1)));
}
#[test]
fn test_configuration_empty_stack() {
let config: PdaConfiguration<char> = PdaConfiguration::new(0, vec![], vec![]);
assert!(config.stack_empty());
assert_eq!(config.stack_top(), None);
}
#[test]
fn test_configuration_input_exhausted() {
let config: PdaConfiguration<char> =
PdaConfiguration::new(0, vec![], vec![StackSymbol::BOTTOM]);
assert!(config.input_exhausted());
}
#[test]
fn test_configuration_next_input() {
let config: PdaConfiguration<char> =
PdaConfiguration::new(0, vec!['a', 'b'], vec![StackSymbol::BOTTOM]);
assert_eq!(config.next_input(), Some(&'a'));
}
#[test]
fn test_apply_transition_consuming() {
let config: PdaConfiguration<char> =
PdaConfiguration::new(0, vec!['a', 'b'], vec![StackSymbol::BOTTOM]);
let trans = PdaTransition::<char, TropicalWeight>::new(
0,
Some('a'),
StackSymbol::BOTTOM,
StackAction::Push(vec![StackSymbol::BOTTOM, StackSymbol::new(1)]),
1,
TropicalWeight::one(),
);
let new_config = config
.apply_transition(&trans)
.expect("pushdown/traits.rs: required value was None/Err");
assert_eq!(new_config.state, 1);
assert_eq!(new_config.remaining_input, vec!['b']);
assert_eq!(
new_config.stack,
vec![StackSymbol::BOTTOM, StackSymbol::new(1)]
);
}
#[test]
fn test_apply_transition_epsilon() {
let config: PdaConfiguration<char> =
PdaConfiguration::new(0, vec!['a'], vec![StackSymbol::BOTTOM]);
let trans = PdaTransition::<char, TropicalWeight>::epsilon(
0,
StackSymbol::BOTTOM,
StackAction::Noop,
1,
TropicalWeight::one(),
);
let new_config = config
.apply_transition(&trans)
.expect("pushdown/traits.rs: required value was None/Err");
assert_eq!(new_config.state, 1);
assert_eq!(new_config.remaining_input, vec!['a']); assert_eq!(new_config.stack, vec![StackSymbol::BOTTOM]);
}
#[test]
fn test_apply_transition_wrong_stack() {
let config: PdaConfiguration<char> =
PdaConfiguration::new(0, vec!['a'], vec![StackSymbol::BOTTOM]);
let trans = PdaTransition::<char, TropicalWeight>::new(
0,
Some('a'),
StackSymbol::new(1), StackAction::Pop,
1,
TropicalWeight::one(),
);
assert!(config.apply_transition(&trans).is_none());
}
#[test]
fn test_apply_transition_wrong_input() {
let config: PdaConfiguration<char> =
PdaConfiguration::new(0, vec!['a'], vec![StackSymbol::BOTTOM]);
let trans = PdaTransition::<char, TropicalWeight>::new(
0,
Some('b'), StackSymbol::BOTTOM,
StackAction::Pop,
1,
TropicalWeight::one(),
);
assert!(config.apply_transition(&trans).is_none());
}
}