use super::ShortFloat;
use crate::{
__impl_to_display_and_display, global::Float, inference::Truth, util::ToDisplayAndBrief,
};
use anyhow::Result;
use narsese::lexical::Truth as LexicalTruth;
use std::{
fmt::Debug,
hash::{Hash, Hasher},
};
#[derive(Debug, Clone, Copy, Default, Eq)]
pub struct TruthValue {
f: ShortFloat,
c: ShortFloat,
a: bool,
}
mod serde {
use super::TruthValue;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
impl Serialize for TruthValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let v = (self.f, self.c, self.a);
v.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TruthValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let (f, c, a) = Deserialize::deserialize(deserializer)?;
Ok(Self { f, c, a })
}
}
}
impl Truth for TruthValue {
#[inline(always)]
fn frequency(&self) -> ShortFloat {
self.f
}
#[inline(always)]
fn frequency_mut(&mut self) -> &mut ShortFloat {
&mut self.f
}
#[inline(always)]
fn confidence(&self) -> ShortFloat {
self.c
}
#[inline(always)]
fn confidence_mut(&mut self) -> &mut ShortFloat {
&mut self.c
}
#[inline(always)]
fn is_analytic(&self) -> bool {
self.a
}
#[inline(always)]
fn set_analytic(&mut self) {
self.a = true;
}
}
impl TruthValue {
#[inline(always)]
pub fn new(frequency: ShortFloat, confidence: ShortFloat, is_analytic: bool) -> Self {
Self {
f: frequency,
c: confidence,
a: is_analytic,
}
}
pub fn from(truth: &impl Truth) -> Self {
Self::new(truth.frequency(), truth.confidence(), truth.is_analytic())
}
pub fn new_fc(frequency: ShortFloat, confidence: ShortFloat) -> Self {
Self::new(frequency, confidence, false)
}
pub fn from_floats(frequency: Float, confidence: Float, is_analytic: bool) -> Self {
Self::new(
ShortFloat::from_float(frequency),
ShortFloat::from_float(confidence),
is_analytic,
)
}
pub fn from_fc(frequency: Float, confidence: Float) -> Self {
Self::new_fc(
ShortFloat::from_float(frequency),
ShortFloat::from_float(confidence),
)
}
pub fn new_analytic_default() -> Self {
Self::new(ShortFloat::HALF, ShortFloat::ZERO, false)
}
pub fn from_lexical(
lexical: LexicalTruth,
mut default_values: [ShortFloat; 2],
is_analytic: bool,
) -> Result<Self> {
let truth_s = match lexical.len() {
0 => &[],
1 => &lexical[0..1],
_ => &lexical[0..2],
};
let float_s = &mut default_values;
for (i, s) in truth_s.iter().enumerate() {
let v = s.parse::<Float>()?;
let sf = match ShortFloat::try_from(v) {
Ok(sf) => sf,
Err(_) => return Err(anyhow::anyhow!("无效短浮点值:{v}")),
};
float_s[i] = sf;
}
let [f, c] = *float_s;
Ok(Self::new(f, c, is_analytic))
}
pub fn to_lexical(&self) -> LexicalTruth {
vec![
self.frequency().to_display_brief(),
self.confidence().to_display_brief(),
]
}
}
impl<T: Truth> From<&T> for TruthValue {
fn from(value: &T) -> Self {
Self::new(value.frequency(), value.confidence(), value.is_analytic())
}
}
__impl_to_display_and_display! {
@(truth_to_display; truth_to_display_brief;)
TruthValue as Truth
}
impl PartialEq for TruthValue {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.f == other.f && self.c == other.c
}
}
impl Hash for TruthValue {
fn hash<H: Hasher>(&self, state: &mut H) {
self.f.hash(state);
self.c.hash(state);
}
}
mod conversion {
#[macro_export]
macro_rules! truth {
($f:expr; $c:expr) => {
TruthValue::from_fc($f, $c)
};
($f:expr; $c:expr; $a:expr) => {
TruthValue::from_floats($f, $c, $a)
};
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ok, truth, util::AResult};
use nar_dev_utils::macro_once;
type TruthV = TruthValue;
type SF = ShortFloat;
#[test]
fn frequency() -> AResult {
macro_once! {
macro test($( [ $($truth:tt)* ] => $expected:tt)*) {
$(
assert_eq!(
truth!($($truth)*).frequency(),
SF::from_float($expected)
);
)*
}
[1.0; 0.9] => 1.0
[0.1; 0.9] => 0.1
[0.0001; 0.9] => 0.0001
[0.1024; 0.0] => 0.1024
[0.2; 0.1] => 0.2
}
ok!()
}
#[test]
fn frequency_mut() -> AResult {
macro_once! {
macro test($( [ $($truth:tt)* ] -> $new_float:tt => $expected:tt)*) {
$(
let mut t = truth!($($truth)*);
*t.frequency_mut() = SF::from_float($new_float);
assert_eq!(t.frequency(), *t.frequency_mut());
assert_eq!(*t.frequency_mut(), SF::from_float($expected));
)*
}
[1.0; 0.9] -> 0.5 => 0.5
[0.1; 0.9] -> 0.2 => 0.2
[0.0001; 0.9] -> 0.8 => 0.8
[0.1024; 0.0] -> 0.0 => 0.0
[0.2; 0.1] -> 1.0 => 1.0
}
ok!()
}
#[test]
fn confidence() -> AResult {
macro_once! {
macro test($( [ $($truth:tt)* ] => $expected:tt)*) {
$(
assert_eq!(
truth!($($truth)*).confidence(),
SF::from_float($expected)
);
)*
}
[1.0; 0.9] => 0.9
[0.1; 0.9] => 0.9
[0.0001; 0.9] => 0.9
[0.1024; 0.0] => 0.0
[0.2; 0.1] => 0.1
}
ok!()
}
#[test]
fn confidence_mut() -> AResult {
macro_once! {
macro test($( [ $($truth:tt)* ] -> $new_float:tt => $expected:tt)*) {
$(
let mut t = truth!($($truth)*);
*t.confidence_mut() = SF::from_float($new_float);
assert_eq!(t.confidence(), *t.confidence_mut());
assert_eq!(*t.confidence_mut(), SF::from_float($expected));
)*
}
[1.0; 0.9] -> 0.5 => 0.5
[0.1; 0.9] -> 0.2 => 0.2
[0.0001; 0.9] -> 0.8 => 0.8
[0.1024; 0.0] -> 0.0 => 0.0
[0.2; 0.1] -> 1.0 => 1.0
}
ok!()
}
#[test]
fn is_analytic() -> AResult {
macro_once! {
macro test($( [ $($truth:tt)* ] => $expected:tt)*) {
$(
assert_eq!(
truth!($($truth)*).is_analytic(),
$expected
);
)*
}
[1.0; 0.9] => false
[1.0; 0.9; false] => false
[1.0; 0.9; true] => true
}
ok!()
}
#[test]
fn set_analytic() -> AResult {
fn test(mut truth: TruthV) {
truth.set_analytic();
assert!(truth.is_analytic());
}
macro_once! {
macro test($( [ $($truth:tt)* ])*) {
$(
test(truth!($($truth)*));
)*
}
[1.0; 0.9]
[1.0; 0.9; false]
[1.0; 0.9; true]
}
ok!()
}
#[test]
fn expectation() -> AResult {
fn test(truth: TruthV, expected: Float) {
assert_eq!(truth.expectation(), expected);
}
macro_once! {
macro test($( [ $($truth:tt)* ] => $expected:tt)*) {
$(
test(truth!($($truth)*), $expected);
)*
}
[0.0; 0.0] => 0.5 [0.0; 0.5] => 0.25 [0.0; 1.0] => 0.0
[0.5; 0.0] => 0.5 [0.5; 0.5] => 0.5 [0.5; 1.0] => 0.5
[1.0; 0.0] => 0.5 [1.0; 0.5] => 0.75 [1.0; 1.0] => 1.0
[1.0; 0.9] => ((0.9 * (1.0 - 0.5)) + 0.5)
}
ok!()
}
#[test]
fn expectation_abs_dif() -> AResult {
fn test(truth1: TruthV, truth2: TruthV, expected: Float) {
assert_eq!(truth1.expectation_abs_dif(&truth2), expected);
}
macro_once! {
macro test($( | [ $($truth1:tt)* ] - [ $($truth2:tt)* ] | => $expected:tt)*) {
$(
test(
truth!($($truth1)*),truth!($($truth2)*),
$expected
);
)*
}
|[0.0; 0.0]-[1.0; 1.0]| => 0.5 |[0.0; 0.5]-[1.0; 0.5]| => 0.5 |[0.0; 1.0]-[1.0; 0.0]| => 0.5
|[0.5; 0.0]-[0.5; 1.0]| => 0.0 |[0.5; 0.5]-[0.5; 0.5]| => 0.0 |[0.5; 1.0]-[0.5; 0.0]| => 0.0
|[1.0; 0.0]-[0.0; 1.0]| => 0.5 |[1.0; 0.5]-[0.0; 0.5]| => 0.5 |[1.0; 1.0]-[0.0; 0.0]| => 0.5
|[1.0; 0.9] - [0.8; 0.3]| => ((1.0*0.9 - 0.8*0.3 - 0.5*(0.9 - 0.3) as Float).abs())
}
ok!()
}
#[test]
fn is_negative() -> AResult {
fn test(truth: TruthV, expected: bool) {
assert_eq!(truth.is_negative(), expected);
}
macro_once! {
macro test($( [ $($truth:tt)* ] => $expected:tt)*) {
$(
test(truth!($($truth)*), $expected);
)*
}
[1.0; 0.9] => false
[0.9; 0.9] => false
[0.8; 0.9] => false
[0.7; 0.9] => false
[0.6; 0.9] => false
[0.5; 0.9] => false
[0.4; 0.9] => true
[0.3; 0.9] => true
[0.2; 0.9] => true
[0.1; 0.9] => true
[0.0; 0.9] => true
}
ok!()
}
#[test]
fn to_display() -> AResult {
macro_once! {
macro test($( [ $($truth:tt)* ] => $expected:tt)*) {
$(
assert_eq!(
truth!($($truth)*).to_display(),
$expected
);
)*
}
[0.0 ; 0.0 ] => "%0.0000;0.0000%"
[1.0 ; 1.0 ] => "%1.0000;1.0000%"
[1.0 ; 0.9 ] => "%1.0000;0.9000%"
[0.9 ; 1.0 ] => "%0.9000;1.0000%"
[0.9 ; 0.9 ] => "%0.9000;0.9000%"
[0.1 ; 0.42 ] => "%0.1000;0.4200%"
[0.137 ; 0.442 ] => "%0.1370;0.4420%"
[0.1024; 0.2185] => "%0.1024;0.2185%"
}
ok!()
}
#[test]
fn to_display_brief() -> AResult {
macro_once! {
macro test($( [ $($truth:tt)* ] => $expected:tt)*) {
$(
assert_eq!(
truth!($($truth)*).to_display_brief(),
$expected
);
)*
}
[0.0 ; 0.0 ] => "%0.00;0.00%"
[1.0 ; 1.0 ] => "%1.00;1.00%"
[1.0 ; 0.9 ] => "%1.00;0.90%"
[0.9 ; 1.0 ] => "%0.90;1.00%"
[0.9 ; 0.9 ] => "%0.90;0.90%"
[0.1 ; 0.42 ] => "%0.10;0.42%"
[0.137 ; 0.442 ] => "%0.14;0.44%" [0.1024; 0.2185] => "%0.10;0.22%" [0.999 ; 0.9999] => "%1.00;1.00%" }
ok!()
}
#[test]
fn from_lexical() -> AResult {
fn test(lexical: LexicalTruth, truth: TruthV, fc: [Float; 2], is_analytic: bool) {
let [f, c] = fc;
let parsed = TruthV::from_lexical(
lexical,
[
ShortFloat::from_float(f),
ShortFloat::from_float(c),
],
is_analytic,
)
.unwrap();
assert_eq!(parsed, truth);
}
macro_once! {
macro test($(
[ $($lexical:tt)* ] @ [$f:expr; $c:expr; $is_analytic:expr]
=> [ $($truth:tt)* ] )*
) {
$(
test(
narsese::lexical_truth!($($lexical)*),
truth!($($truth)*),
[$f, $c],
$is_analytic
);
)*
}
["1.0" "0.9"] @ [0.0; 0.0; false] => [1.0; 0.9; false]
["1.0" "0.9"] @ [0.0; 0.0; true] => [1.0; 0.9; true]
["0.0" "0.0"] @ [1.0; 0.9; false] => [0.0; 0.0; false]
["0.0" "0.0"] @ [1.0; 0.9; true] => [0.0; 0.0; true]
["0.0"] @ [1.0; 0.9; true] => [0.0; 0.9; true]
[] @ [1.0; 0.9; true] => [1.0; 0.9; true]
["0.0" "0.1" "0.2"] @ [1.0; 0.9; true] => [0.0; 0.1; true]
["0.0" "0.1" "0.2" "0.3"] @ [1.0; 0.9; true] => [0.0; 0.1; true]
["0.0" "0.1" "ARCJ" "137442"] @ [1.0; 0.9; true] => [0.0; 0.1; true]
}
ok!()
}
}