Skip to content

Commit

Permalink
Add operator overloading for Python float and decimal
Browse files Browse the repository at this point in the history
- Parse floats with the `num` function
- Evaluate powers of floats
  • Loading branch information
benruijl committed Jun 6, 2024
1 parent 07a32d2 commit 7b82f78
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 59 deletions.
41 changes: 32 additions & 9 deletions src/api/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::{
domains::{
atom::AtomField,
finite_field::{ToFiniteField, Zp},
float::Complex,
float::{Complex, Float},
integer::{Integer, IntegerRing, Z},
rational::{Rational, RationalField, Q},
rational_polynomial::{
Expand Down Expand Up @@ -1156,6 +1156,24 @@ impl<'a> FromPyObject<'a> for ConvertibleToExpression {
let a = format!("{}", num);
let i = Integer::from_large(rug::Integer::parse(&a).unwrap().complete());
Ok(ConvertibleToExpression(Atom::new_num(i).into()))
} else if let Ok(f) = ob.extract::<f64>() {
if !f.is_finite() {
return Err(exceptions::PyValueError::new_err("Number must be finite"));
}

Ok(ConvertibleToExpression(
Atom::new_num(rug::Float::with_val(53, f)).into(),
))
} else if ob.is_instance(get_decimal(ob.py()).as_ref(ob.py()))? {
let a = ob.call_method0("__str__").unwrap().extract::<&str>()?;
Ok(ConvertibleToExpression(
Atom::new_num(
Float::parse(a)
.unwrap()
.complete((a.len() as f64 * LOG2_10).ceil() as u32),
)
.into(),
))
} else {
Err(exceptions::PyValueError::new_err(
"Cannot convert to expression",
Expand Down Expand Up @@ -1495,8 +1513,8 @@ impl PythonExpression {
Ok(result)
}

/// Create a new Symbolica number from an int or a float.
/// A floating point number is converted to its rational number equivalent,
/// Create a new Symbolica number from an int, a float, or a string.
/// A floating point number is kept as a float with the same precision as the input,
/// but it can also be truncated by specifying the maximal denominator value.
///
/// Examples
Expand All @@ -1505,9 +1523,9 @@ impl PythonExpression {
/// >>> print(e)
/// 1/2
///
/// >>> print(Expression.num(0.33))
/// >>> print(Expression.num(1/3))
/// >>> print(Expression.num(0.33, 5))
/// 5944751508129055/18014398509481984
/// 3.3333333333333331e-1
/// 1/3
#[classmethod]
pub fn num(
Expand All @@ -1526,12 +1544,17 @@ impl PythonExpression {
return Err(exceptions::PyValueError::new_err("Number must be finite"));
}

let mut r: Rational = f.into();
if let Some(max_denom) = max_denom {
r = r.truncate_denominator(&(max_denom as u64).into())
let mut r: Rational = f.into();
r = r.truncate_denominator(&(max_denom as u64).into());
Ok(Atom::new_num(r).into())
} else {
Ok(Atom::new_num(rug::Float::with_val(53, f)).into())
}

Ok(Atom::new_num(r).into())
} else if let Ok(f) = num.extract::<PythonMultiPrecisionFloat>(py) {
Ok(Atom::new_num(f.0.clone()).into())
} else if let Ok(a) = num.extract::<&str>(py) {
PythonExpression::parse(_cls, a)
} else {
Err(exceptions::PyValueError::new_err("Not a valid number"))
}
Expand Down
45 changes: 45 additions & 0 deletions src/coefficient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ impl From<Rational> for Coefficient {
}
}

impl From<Float> for Coefficient {
fn from(value: Float) -> Self {
Coefficient::Float(value)
}
}

impl Default for Coefficient {
fn default() -> Self {
Coefficient::zero()
Expand Down Expand Up @@ -520,6 +526,45 @@ impl CoefficientView<'_> {
(r.to_rat().pow(n2 as u32).into(), (1, d2).into())
}
}
(&CoefficientView::Float(f), &CoefficientView::Natural(n2, d2)) => {
let f = f.to_float();
let p = f.prec();
(
f.pow(Rational::new(n2, d2).to_multi_prec_float(p)).into(),
Coefficient::one(),
)
}
(&CoefficientView::Float(f), &CoefficientView::Large(r)) => {
let f = f.to_float();
let p = f.prec();
(
f.pow(Rational::from_large(r.to_rat()).to_multi_prec_float(p))
.into(),
Coefficient::one(),
)
}
(&CoefficientView::Natural(n2, d2), &CoefficientView::Float(f)) => {
let f = f.to_float();
let p = f.prec();
(
Rational::new(n2, d2).to_multi_prec_float(p).pow(f).into(),
Coefficient::one(),
)
}
(&CoefficientView::Large(r), &CoefficientView::Float(f)) => {
let f = f.to_float();
let p = f.prec();
(
Rational::from_large(r.to_rat())
.to_multi_prec_float(p)
.pow(f)
.into(),
Coefficient::one(),
)
}
(&CoefficientView::Float(f1), &CoefficientView::Float(f2)) => {
(f1.to_float().pow(f2.to_float()).into(), Coefficient::one())
}
_ => {
unimplemented!(
"Power of configuration {:?}^{:?} is not implemented",
Expand Down
Loading

0 comments on commit 7b82f78

Please sign in to comment.