Skip to main content

Ex

Type Alias Ex 

Source
pub type Ex = Expr<Numeric>;
Expand description

A numeric expression — the primary type for symbolic math.

Aliased Type§

pub struct Ex { /* private fields */ }

Implementations§

Source§

impl Ex

Source

pub fn singularities( &self, var: &Ex, domain: Option<&SetEx>, ) -> Result<SetEx, SymplexError>

The points of domain (default ℝ) where self is undefined — SymPy’s singularities.

The rule set is SymPy’s: zeros of the base of every negative power (this covers denominators, sec, csc and cot), zeros of the argument of ln, poles of tan, and atanh(g) at g = ±1. Only real points are reported.

Zeros are found exactly with solve; equations with sin/cos/tan of var use solve_general and the periodic families are enumerated inside a bounded domain (tan(x) on [0, 10] gives {π/2, 3π/2, 5π/2}). On an unbounded domain such a family is returned as the condition set {x | cos(x) = 0} (intersected with the domain), since the infinite family has no interval / finite-set representation.

§Errors
  • InvalidArgumentvar is not a symbol.
  • ComputationFailed — the zeros of some source cannot be found exactly (the solver fails on g = 0), or, on the periodic-family path only, a family is not linear in its integer parameter, its members cannot be located numerically, more than 10 000 of them may lie in the domain, or the membership of a member in domain cannot be decided. Plain (non-periodic) zeros whose membership in domain is undecided do not error: the result is then Ok with the intersection {p, …} ∩ domain left unevaluated.
§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
// SymPy: singularities(1/(x**2 - 1), x) == {-1, 1}
let s = (1 / (&x.powi(2) - 1)).singularities(&x, None).unwrap();
assert_eq!(s.to_string(), "{-1, 1}");
// SymPy: singularities(log(x), x) == {0}
assert_eq!(x.ln().singularities(&x, None).unwrap().to_string(), "{0}");
// Polynomials have none.
assert_eq!(x.powi(2).singularities(&x, None).unwrap().is_empty(), Some(true));
// Restricted to a domain.
let dom = ctx.interval(&ctx.int(0), &ctx.int(5), false, false);
assert_eq!((1 / (&x.powi(2) - 1)).singularities(&x, Some(&dom)).unwrap().to_string(), "{1}");
Source

pub fn stationary_points( &self, var: &Ex, domain: Option<&SetEx>, ) -> Result<SetEx, SymplexError>

The real solutions of d self / d var = 0 in domain (default ℝ) — SymPy’s stationary_points.

Periodic families of critical points (sin, cos, tan) are enumerated on a bounded domain and returned as a condition set {x | f'(x) = 0} on an unbounded one; see singularities. An expression that does not depend on var has derivative 0, so every point of the domain is stationary and the domain itself is returned (as SymPy does).

The sign(h) factors that differentiating |h| introduces are resolved by cases: on each region where every h has a fixed sign the derivative is a plain expression whose zeros are found and kept where the assumed signs hold (|x − 1| + x²{1/2}). A region on which the derivative vanishes identically is returned whole (|x| + x on [−1, 2][−1, 0)), and a kink at which the derivative evaluates to zero counts as stationary (|x|{0}, since sign(0) = 0) — all as SymPy does. At most four distinct sign factors are resolved.

§Errors
  • InvalidArgumentvar is not a symbol.
  • ComputationFailed — the derivative is a formal Derivative, the zeros of the derivative cannot be found exactly, or more than four sign factors would have to be resolved.
§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let f = &x.powi(3) - &x * 3;
// SymPy: stationary_points(x**3 - 3*x, x) == {-1, 1}
assert_eq!(f.stationary_points(&x, None).unwrap().to_string(), "{-1, 1}");
// SymPy: stationary_points(x**3 - 3*x, x, Interval(0, 5)) == {1}
let dom = ctx.interval(&ctx.int(0), &ctx.int(5), false, false);
assert_eq!(f.stationary_points(&x, Some(&dom)).unwrap().to_string(), "{1}");
// SymPy: stationary_points(sin(x), x, Interval(0, 2*pi)) == {pi/2, 3*pi/2}
let two_pi = ctx.interval(&ctx.int(0), &(&ctx.pi() * 2), false, false);
let sp = x.sin().stationary_points(&x, Some(&two_pi)).unwrap();
assert_eq!(sp.as_finite_set().unwrap().len(), 2);
// SymPy: stationary_points(Abs(x - 1) + x**2, x, Interval(-1, 2)) == {1/2}
let g = (&x - 1).abs() + x.powi(2);
let dom = ctx.interval(&ctx.int(-1), &ctx.int(2), false, false);
assert_eq!(g.stationary_points(&x, Some(&dom)).unwrap().to_string(), "{1/2}");
Source

pub fn maximum(&self, var: &Ex, domain: &SetEx) -> Result<Ex, SymplexError>

Supremum of self (continuous in var) over domain, a union of intervals — SymPy’s maximum.

The candidates are the values at the stationary points and abs kinks inside the domain (see stationary_points for how |h| is handled: maximum(|x|, [−1, 2]) = 2, minimum(|x − 1| + x², [−1, 2]) = 3/4), at closed endpoints, and the one-sided limits at open or infinite endpoints; +∞ / −∞ are legitimate results. Candidates are compared exactly (see the module notes for the numeric fallbacks). The supremum need not be attained: maximum(x, (0, 1)) = 1.

§Errors
  • InvalidArgumentvar is not a symbol, or domain is empty or not a union of intervals.
  • ComputationFailedself has singularities inside the domain, contains a discontinuous or opaque node (floor, sign, Piecewise, an unknown function, …), its stationary points cannot be enumerated, an endpoint limit cannot be computed, or two candidates cannot be compared.
§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let f = &x.powi(3) - &x * 3;
let dom = ctx.interval(&ctx.int(-2), &ctx.int(2), false, false);
// SymPy: maximum(x**3 - 3*x, x, Interval(-2, 2)) == 2
assert_eq!(f.maximum(&x, &dom).unwrap().to_string(), "2");
// SymPy: maximum(x**2, x, S.Reals) == oo
assert_eq!(x.powi(2).maximum(&x, &ctx.reals()).unwrap(), ctx.infinity());
// SymPy: maximum(1/x, x, Interval(1, oo)) == 1
let tail = ctx.interval(&ctx.int(1), &ctx.infinity(), false, true);
assert_eq!((1 / &x).maximum(&x, &tail).unwrap().to_string(), "1");
Source

pub fn minimum(&self, var: &Ex, domain: &SetEx) -> Result<Ex, SymplexError>

Infimum of self (continuous in var) over domain — SymPy’s minimum. Same method, candidates and errors as maximum.

§Errors

See maximum.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let f = &x.powi(3) - &x * 3;
let dom = ctx.interval(&ctx.int(-2), &ctx.int(2), false, false);
// SymPy: minimum(x**3 - 3*x, x, Interval(-2, 2)) == -2
assert_eq!(f.minimum(&x, &dom).unwrap().to_string(), "-2");
// SymPy: minimum(1/x, x, Interval(1, oo)) == 0   (a limit, not attained)
let tail = ctx.interval(&ctx.int(1), &ctx.infinity(), false, true);
assert_eq!((1 / &x).minimum(&x, &tail).unwrap().to_string(), "0");
// SymPy: minimum(x**2, x, S.Reals) == 0
assert_eq!(x.powi(2).minimum(&x, &ctx.reals()).unwrap().to_string(), "0");
Source

pub fn is_increasing(&self, var: &Ex, domain: &SetEx) -> Option<bool>

Is self non-decreasing in var on domain (f' ≥ 0 there)? SymPy’s is_increasing.

Three-valued. Polynomial and rational derivatives with rational coefficients are decided exactly (Sturm sequences on each interval of the domain, the denominator having constant sign there); otherwise the assumption system and the inequality solver (solve_ge) are consulted. A derivative that is undefined at a closed endpoint (√x at 0) is tested on the interior instead, provided the function is continuous there. None means undecided — never a guess; expressions with discontinuous or opaque nodes (floor, sign, unknown functions) are always None. The domain must be a union of intervals (None otherwise); the empty domain is vacuously Some(true).

A pole strictly inside the domain refutes monotonicity regardless of the sign of f' on either side: 1/x is not decreasing on [−1, 1] (f(−1) = −1 < 1 = f(1)) and tan x is not increasing on [0, π], although f' < 0 resp. f' > 0 wherever it is defined. (SymPy tests the derivative alone and answers True for is_increasing(tan(x), Interval(0, pi)).) When the singularities inside the domain cannot be enumerated (tan x on ℝ) the answer is None.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let reals = ctx.reals();
// SymPy: is_increasing(x**3, S.Reals, x) is True
assert_eq!(x.powi(3).is_increasing(&x, &reals), Some(true));
// SymPy: is_increasing(x**2, S.Reals, x) is False
assert_eq!(x.powi(2).is_increasing(&x, &reals), Some(false));
// SymPy: is_increasing(x**2, Interval(0, oo), x) is True
let half = ctx.interval(&ctx.int(0), &ctx.infinity(), false, true);
assert_eq!(x.powi(2).is_increasing(&x, &half), Some(true));
// SymPy: is_increasing(exp(x), S.Reals, x) is True
assert_eq!(x.exp().is_increasing(&x, &reals), Some(true));
Source

pub fn is_decreasing(&self, var: &Ex, domain: &SetEx) -> Option<bool>

Is self non-increasing in var on domain (f' ≤ 0 there)? SymPy’s is_decreasing. Same method as is_increasing.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
// SymPy: is_decreasing(x**2, Interval(-oo, 0), x) is True
let left = ctx.interval(&ctx.neg_infinity(), &ctx.int(0), true, false);
assert_eq!(x.powi(2).is_decreasing(&x, &left), Some(true));
// SymPy: is_decreasing(1/x, Interval.open(0, oo), x) is True
let pos = ctx.interval(&ctx.int(0), &ctx.infinity(), true, true);
assert_eq!((1 / &x).is_decreasing(&x, &pos), Some(true));
assert_eq!(x.powi(3).is_decreasing(&x, &ctx.reals()), Some(false));
Source

pub fn is_strictly_increasing(&self, var: &Ex, domain: &SetEx) -> Option<bool>

Is self strictly increasing in var on domain? SymPy’s is_strictly_increasing.

Decided as f' ≥ 0 with only isolated zeros: for polynomial and rational derivatives this is exact ( is strictly increasing on ℝ although f'(0) = 0, and so is on [0, ∞)); otherwise Some(true) needs f' > 0 on the domain or a finite zero set from the inequality solver, Some(false) needs f' < 0 somewhere, and anything else is None. (SymPy tests domain ⊆ {f' > 0} and answers None / False for on ℝ; the mathematically correct answer is returned here.)

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
assert_eq!(x.powi(3).is_strictly_increasing(&x, &ctx.reals()), Some(true));
assert_eq!(x.powi(2).is_strictly_increasing(&x, &ctx.reals()), Some(false));
// A constant is increasing but not strictly.
assert_eq!(ctx.int(3).is_increasing(&x, &ctx.reals()), Some(true));
assert_eq!(ctx.int(3).is_strictly_increasing(&x, &ctx.reals()), Some(false));
Source

pub fn is_strictly_decreasing(&self, var: &Ex, domain: &SetEx) -> Option<bool>

Is self strictly decreasing in var on domain? SymPy’s is_strictly_decreasing; see is_strictly_increasing.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
assert_eq!((-&x.powi(3)).is_strictly_decreasing(&x, &ctx.reals()), Some(true));
let pos = ctx.interval(&ctx.int(0), &ctx.infinity(), true, true);
assert_eq!((1 / &x).is_strictly_decreasing(&x, &pos), Some(true));
Source

pub fn is_monotonic(&self, var: &Ex, domain: &SetEx) -> Option<bool>

Is self monotonic (non-decreasing or non-increasing) in var on domain? SymPy’s is_monotonic.

The three-valued disjunction of is_increasing and is_decreasing: Some(true) when either is proven, Some(false) when both are refuted, None otherwise. (SymPy’s is_monotonic instead asks whether f' has no zeros in the domain and therefore answers False for on ℝ.)

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
assert_eq!(x.powi(3).is_monotonic(&x, &ctx.reals()), Some(true));
assert_eq!((-&x).is_monotonic(&x, &ctx.reals()), Some(true));
assert_eq!(x.powi(2).is_monotonic(&x, &ctx.reals()), Some(false));
Source

pub fn is_convex(&self, var: &Ex, domain: &SetEx) -> Option<bool>

Is self convex in var on domain (f'' ≥ 0 there)? SymPy’s is_convex for one variable.

Same machinery as is_increasing, applied to the first derivative. Three-valued.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
// SymPy: is_convex(x**2, x) is True, is_convex(x**3, x) is False
assert_eq!(x.powi(2).is_convex(&x, &ctx.reals()), Some(true));
assert_eq!(x.powi(3).is_convex(&x, &ctx.reals()), Some(false));
// SymPy: is_convex(x**3, x, domain=Interval(0, oo)) is True
let half = ctx.interval(&ctx.int(0), &ctx.infinity(), false, true);
assert_eq!(x.powi(3).is_convex(&x, &half), Some(true));
assert_eq!(x.exp().is_convex(&x, &ctx.reals()), Some(true));
Source

pub fn periodicity(&self, var: &Ex) -> Option<Ex>

A period of self in var — SymPy’s periodicity. Like SymPy’s, the value is a period, not necessarily the fundamental one: composite expressions get the lcm of the periods of their pieces, and identities that shorten the period are not detected (sin²x·cos²x = sin²(2x)/4 gives π, whose fundamental period is π/2 — SymPy answers π/2 here through its own simplification).

  • Some(0) when self does not depend on var.
  • sin(a·x + b), cos(a·x + b)2π/|a|; tan(a·x + b)π/|a|; sec, csc, cot (which are built from sin/cos) follow, with products sin(g)ᵖ·cos(g)ᵠ of even exponent sum (sin·cos, cos/sin, sin²) and |sin g|, |cos g| getting the half period π/|a|.
  • Sums, products, powers and compositions (exp(sin x), sin(2x) + cos(3x)) take the lcm of the periods of their var-dependent parts; the lcm needs pairwise rational ratios.
  • None when a var-dependent part is not recognised as periodic (, sin(x²), sin(x) + x, sin(√2·x) + sin(x)).

The expression is simplified first (sin²x + cos²x1Some(0)); the original form is tried if the simplified one is not recognised. Note that SymPy reports for sin(x)²; the half-period rule gives π here.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let p = |e: &Ex| e.periodicity(&x).map(|p| p.to_string());
// SymPy: periodicity(sin(2*x) + cos(3*x), x) == 2*pi
assert_eq!(p(&(&(&x * 2).sin() + &(&x * 3).cos())), Some("2*pi".into()));
// SymPy: periodicity(tan(x), x) == pi
assert_eq!(p(&x.tan()), Some("pi".into()));
// SymPy: periodicity(sin(3*x + 1), x) == 2*pi/3
assert_eq!(p(&(&x * 3 + 1).sin()), Some("2/3*pi".into()));
// SymPy: periodicity(S(3), x) == 0; periodicity(x**2, x) is None
assert_eq!(p(&ctx.int(3)), Some("0".into()));
assert_eq!(p(&x.powi(2)), None);
Source

pub fn function_range( &self, var: &Ex, domain: &SetEx, ) -> Result<SetEx, SymplexError>

The image of self (continuous in var) over domain, a union of intervals — SymPy’s function_range.

On each interval of the domain the infimum and supremum are found as in minimum / maximum; the image of that interval is [inf, sup] with an endpoint open exactly when the value is only approached (a one-sided limit at an open or infinite endpoint that is not also attained elsewhere) or infinite. The pieces are united and simplified.

§Errors
  • InvalidArgumentvar is not a symbol, or domain is not a union of intervals (the empty domain gives the empty set).
  • ComputationFailed — as for maximum.
§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let r = |f: &Ex, d: &SetEx| f.function_range(&x, d).unwrap().to_string();
// SymPy: function_range(sin(x), x, Interval(0, pi)) == Interval(0, 1)
assert_eq!(r(&x.sin(), &ctx.interval(&ctx.int(0), &ctx.pi(), false, false)), "[0, 1]");
// SymPy: function_range(x**2, x, S.Reals) == Interval(0, oo)
assert_eq!(r(&x.powi(2), &ctx.reals()), "[0, oo)");
// SymPy: function_range(1/x, x, Interval(1, oo)) == Interval.Lopen(0, 1)
let tail = ctx.interval(&ctx.int(1), &ctx.infinity(), false, true);
assert_eq!(r(&(1 / &x), &tail), "(0, 1]");
// SymPy: function_range(exp(x), x, S.Reals) == Interval.open(0, oo)
assert_eq!(r(&x.exp(), &ctx.reals()), "(0, oo)");
Source§

impl Ex

Source

pub fn as_rational(&self) -> Option<Ratio<BigInt>>

The exact value if this expression is a numeric literal.

Returns None for anything that is not a plain number node (symbols, pi, sqrt(2), unevaluated sums, …) — call eval first if you want constant folding.

use symplex::prelude::*;
use num_bigint::BigInt;
use num_rational::Ratio;

let ctx = Context::new();
let r = ctx.rational(6, 4).as_rational().unwrap();
assert_eq!(r, Ratio::new(BigInt::from(3), BigInt::from(2)));
assert!(ctx.pi().as_rational().is_none());
assert!((&ctx.int(2).sqrt() * &ctx.int(2).sqrt()).eval().as_rational().is_some());
Source

pub fn as_ratio_parts(&self) -> Option<(BigInt, BigInt)>

Numerator and denominator (lowest terms, denominator positive) if this expression is a rational literal — SymPy’s Rational.p / .q.

Unlike as_numer_denom, which decomposes any expression symbolically, this returns plain integers and only for numbers. Call eval first to fold constant arithmetic such as 1/3 + 1/6.

use symplex::prelude::*;
use symplex::num_bigint::BigInt;

let ctx = Context::new();
let (p, q) = ctx.rational(6, -4).as_ratio_parts().unwrap();
assert_eq!((p, q), (BigInt::from(-3), BigInt::from(2)));
assert_eq!(ctx.int(7).as_ratio_parts(), Some((BigInt::from(7), BigInt::from(1))));
assert!(ctx.symbol("x").as_ratio_parts().is_none());
Source

pub fn as_ratio_i128(&self) -> Option<(i128, i128)>

Numerator and denominator as machine integers, if this expression is a rational literal whose parts fit in i128.

The convenient form for comparing with literals or feeding other exact-arithmetic code without touching BigInt:

use symplex::prelude::*;

let ctx = Context::new();
assert_eq!(ctx.rational(3, 31).as_ratio_i128(), Some((3, 31)));
assert_eq!((ctx.rational(1, 3) + ctx.rational(1, 6)).as_ratio_i128(), Some((1, 2)));
assert_eq!(ctx.int(-4).as_ratio_i128(), Some((-4, 1)));
// Too large for i128 → None (use `as_ratio_parts`).
assert!(ctx.from_bigint(symplex::num_bigint::BigInt::from(2).pow(200)).as_ratio_i128().is_none());
Source

pub fn as_bigint(&self) -> Option<BigInt>

The exact value if this expression is an integer literal.

use symplex::prelude::*;
use num_bigint::BigInt;

let ctx = Context::new();
assert_eq!(ctx.int(-7).as_bigint(), Some(BigInt::from(-7)));
assert_eq!(ctx.rational(1, 2).as_bigint(), None);
Source

pub fn as_i64(&self) -> Option<i64>

The value if this expression is an integer literal that fits in i64.

use symplex::prelude::*;

let ctx = Context::new();
assert_eq!(ctx.int(42).as_i64(), Some(42));
assert_eq!(ctx.from_u64(u64::MAX).as_i64(), None);
assert_eq!(ctx.symbol("n").as_i64(), None);
Source

pub fn compare_numeric(&self, other: &Ex) -> Option<Ordering>

Three-valued numeric comparison of self and other.

Decision procedure, in order:

  1. Both are numeric literals → exact rational comparison.
  2. d = (self − other).eval() is a literal → exact sign of d; if d is oo / -ooGreater / Less.
  3. The assumption system knows the sign of d (e.g. a − b with a positive and b negative; x² + 1 for real x).
  4. Both are constants (no free symbols) → 16-digit numeric evaluation; decided only if the values differ by more than 1e-9 relative and both are real.
  5. Otherwise None.
§Examples
use std::cmp::Ordering;
use symplex::prelude::*;

let ctx = Context::new();
assert_eq!(ctx.rational(1, 3).compare_numeric(&ctx.rational(1, 2)), Some(Ordering::Less));
assert_eq!(ctx.pi().compare_numeric(&ctx.int(3)), Some(Ordering::Greater));
assert_eq!(ctx.int(2).sqrt().compare_numeric(&ctx.rational(3, 2)), Some(Ordering::Less));

let x = ctx.symbol("x");
assert_eq!((&x + 1).compare_numeric(&x), Some(Ordering::Greater));
assert_eq!(x.compare_numeric(&ctx.int(0)), None);

let p = ctx.symbol_with("p", &[Assumption::Positive]);
assert_eq!(p.compare_numeric(&ctx.int(0)), Some(Ordering::Greater));
Source

pub fn is_less_than(&self, other: &Ex) -> Option<bool>

Is self < other? Three-valued; see compare_numeric for the decision procedure.

use symplex::prelude::*;

let ctx = Context::new();
assert_eq!(ctx.int(1).is_less_than(&ctx.int(2)), Some(true));
assert_eq!(ctx.pi().is_less_than(&ctx.int(3)), Some(false));
let x = ctx.symbol("x");
assert_eq!(x.is_less_than(&ctx.int(3)), None);
// Assumptions help: x² ≥ 0 for real x, so x² < -1 is false.
let r = ctx.symbol_with("r", &[Assumption::Real]);
assert_eq!(r.powi(2).is_less_than(&ctx.int(-1)), Some(false));
Source

pub fn is_greater_than(&self, other: &Ex) -> Option<bool>

Is self > other? Three-valued; see compare_numeric.

use symplex::prelude::*;

let ctx = Context::new();
assert_eq!(ctx.e().is_greater_than(&ctx.int(2)), Some(true));
assert_eq!(ctx.int(2).is_greater_than(&ctx.int(2)), Some(false));
assert_eq!(ctx.symbol("x").is_greater_than(&ctx.int(0)), None);
Source

pub fn probably_equal(&self, other: &Ex, samples: usize) -> Option<bool>

Randomized equality test: evaluate both sides at samples random rational points and compare.

  • Some(false) — a concrete point was found where the two sides differ. When both sides fold to exact rationals at that point this is a proof; when transcendental functions force floating-point evaluation the sides differ by more than 1e-9 relative.
  • Some(true)equals proved it symbolically, or every sample agreed. The latter is probabilistic: for polynomial and rational identities the chance of a false positive is negligible after a few samples, but no proof is produced.
  • None — no sample point could be evaluated (domain errors at every point) and the symbolic test was inconclusive.

Sample points are drawn from a fixed-seed generator keyed on the two expressions, so results are reproducible. samples == 0 is treated as 1.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let lhs = (&x + 1).powi(3);
let rhs = &x.powi(3) + &x.powi(2) * 3 + &x * 3 + 1;
assert_eq!(lhs.probably_equal(&rhs, 5), Some(true));
assert_eq!(x.probably_equal(&ctx.symbol("y"), 5), Some(false));
assert_eq!(x.sin().probably_equal(&x.cos(), 5), Some(false));
Source

pub fn eval_at(&self, pairs: &[(&Ex, &Ex)]) -> Ex

Substitute (symbol, value) pairs simultaneously and evaluate.

Shorthand for self.subs_map(pairs).eval(). The result is exact and may still be symbolic if not every symbol was bound.

use symplex::prelude::*;

let ctx = Context::new();
let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
let f = &x.powi(2) + &y;
assert_eq!(format!("{}", f.eval_at(&[(&x, &ctx.int(3)), (&y, &ctx.rational(1, 2))])), "19/2");
assert_eq!(format!("{}", f.eval_at(&[(&x, &ctx.pi())])), "y + pi^2");
Source§

impl Ex

Source

pub fn solve_general(&self, var: &Ex) -> Result<GeneralSolution, SymplexError>

Solve self = 0 for var, returning general solution families.

Unlike solve, which returns only principal branches, this expresses periodic solutions with a fresh integer parameter (n, or n1, n2, … if n is already in use), exposed through GeneralSolution::parameters:

  • sin(x) = casin(c) + 2πn, π − asin(c) + 2πn
  • cos(x) = c±acos(c) + 2πn
  • tan(x) = catan(c) + πn

Linear arguments (sin(a·x + b) = c) and change-of-variable forms (sin²x − sin x = 0) are supported. Non-periodic equations return the same solutions as solve with an empty parameter list.

§Errors

Same as solve: InfiniteSolutions for identities, NoSolution for contradictions, ComputationFailed when nothing applies.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let eq = &x.sin() - &ctx.rational(1, 2);
let fam = eq.solve_general(&x).unwrap();
assert_eq!(fam.solutions.len(), 2);
assert_eq!(fam.parameters.len(), 1);
// Every member of every family satisfies the equation.
for k in -2..=2 {
    for s in fam.instance(k) {
        let residual = eq.subs(&x, &s).eval_f64().unwrap();
        assert!(residual.abs() < 1e-12);
    }
}
Source§

impl Ex

Source

pub fn solve_ode_ivp( &self, func: &Ex, var: &Ex, ics: &[(usize, Ex, Ex)], ) -> Result<Ex, SymplexError>

Solve the ODE self = 0 for func(var) subject to initial conditions.

Each initial condition is (k, x0, value) meaning d^k func / d var^k (x0) = value (k = 0 is func(x0) = value). The general solution is found with solve_ode, then the integration constants C1, C2, … are determined by substituting the conditions and solving the resulting (usually linear) system with linsolve; nonlinear constant equations are handled one at a time with solve. Constants not pinned down by the conditions remain in the result.

§Errors
§Examples
use symplex::prelude::*;

let ctx = Context::new();
let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
// y'' + y = 0, y(0) = 0, y'(0) = 1  →  y = sin(x)
let ode = &y.formal_diff(&x).formal_diff(&x) + &y;
let sol = ode
    .solve_ode_ivp(&y, &x, &[(0, ctx.int(0), ctx.int(0)), (1, ctx.int(0), ctx.int(1))])
    .unwrap();
assert_eq!(format!("{}", sol.simplify()), "sin(x)");
Source§

impl Ex

Source

pub fn solve_riccati( &self, func: &Ex, var: &Ex, particular: &Ex, ) -> Result<Ex, SymplexError>

Solve the Riccati equation self = 0, i.e. y' = q₀(x) + q₁(x)·y + q₂(x)·y², given a known particular solution particular.

The substitution y = y_p + 1/v reduces the equation to the linear ODE v' + (q₁ + 2·q₂·y_p)·v = −q₂; the result is y_p + 1/v with the integration constant C1.

§Errors
§Examples
use symplex::prelude::*;

let ctx = Context::new();
let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
// y' = y² - 2/x² has the particular solution y = 1/x
let ode = &y.formal_diff(&x) - &y.powi(2) + &(&ctx.int(2) / &x.powi(2));
let sol = ode.solve_riccati(&y, &x, &(&ctx.int(1) / &x)).unwrap();
assert!(sol.contains(&ctx.symbol("C1")));
assert!(ode.check_ode_solution(&sol, &y, &x));
Source§

impl Ex

Source

pub fn as_poly(&self, gens: &[&Ex]) -> Option<Poly>

View this expression as a Poly in gens; see Poly::new.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let (x, a) = (ctx.symbol("x"), ctx.symbol("a"));
let p = (&a * &x.powi(2) + 1).as_poly(&[&x]).unwrap();
assert_eq!(p.degree_in(&x), Some(2));
assert_eq!(p.leading_coeff(), a);
assert!(x.sin().as_poly(&[&x]).is_none());
Source§

impl Ex

Source

pub fn z_transform(&self, n: &Ex, z: &Ex) -> Result<Ex, SymplexError>

Unilateral Z-transform X(z) = Σ_{n≥0} x[n] z^{−n} of this sequence (a function of the integer index n).

Table: constants, aⁿ, nᵏ aⁿ (via Z{n x[n]} = −z X′(z)), sin(ωn), cos(ωn), aⁿ sin(ωn), aⁿ cos(ωn), H(n − k), δ[n − k], C(n, k), 1/n!; rules: linearity, scaling aⁿ x[n] → X(z/a), delay x[n − k] H(n − k) → z^{−k} X(z).

§Errors

ComputationFailed if n/z are not symbols or no rule applies. There is no unevaluated Z-transform node, so this API is Result-only.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let n = ctx.symbol("n");
let z = ctx.symbol("z");
let half = ctx.rational(1, 2);
// Z{(1/2)^n} = z/(z − 1/2)
let result = half.pow(&n).z_transform(&n, &z).unwrap();
assert_eq!(result, &z / (&z - half));
// n² → z(z + 1)/(z − 1)³
let x = n.powi(2).z_transform(&n, &z).unwrap();
let expected = &z * (&z + 1) / (&z - 1).powi(3);
assert!((&x - &expected).simplify().is_zero_structural(), "{x}");
// δ[n − 3] → z⁻³
assert_eq!((&n - 3).dirac_delta().z_transform(&n, &z).unwrap(), z.powi(-3));
Source

pub fn inverse_z_transform(&self, z: &Ex, n: &Ex) -> Result<Ex, SymplexError>

Inverse (unilateral) Z-transform of this expression (a function of z) as a sequence in n.

Rational X(z) is handled through partial fractions in z (z/(z − a)ᵐ → C(n, m−1) a^{n−m+1}, 1/(z − a)ᵐ through the delay rule), together with constants (δ[n]), z^{−k} (δ[n − k]), z^{−k} X(z) (x[n−k] H(n−k)), e^{1/z} (1/n!) and the trigonometric forms.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let n = ctx.symbol("n");
let z = ctx.symbol("z");
// Z⁻¹{z/(z−2)} = 2ⁿ
let xz = &z / &(&z - 2);
assert_eq!(format!("{}", xz.inverse_z_transform(&z, &n).unwrap()), "2^n");
// Z⁻¹{z⁻²} = δ[n − 2]
let d = (1 / z.powi(2)).inverse_z_transform(&z, &n).unwrap();
assert_eq!(format!("{d}"), "KroneckerDelta(2, n)");
Source§

impl Ex

Source

pub fn prove_nonnegative_on_box( &self, bounds: &[(Ex, Ex, Ex)], degree: u32, ) -> Result<BoxOutcome, SymplexError>

Source§

impl Ex

Source

pub fn find_root_bracket( &self, var: &Ex, a: f64, b: f64, ) -> Result<f64, SymplexError>

Numerically find a root of this expression in var inside the bracket [a, b] by brent_root with default RootOpts.

The expression is compiled with compile first, so evaluation is fast and the usual compile-time checks apply.

§Errors
§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let r = (&x.powi(2) - 2).find_root_bracket(&x, 0.0, 2.0).unwrap();
assert!((r - 2f64.sqrt()).abs() < 1e-12);

// A transcendental equation: cos x = x.
let r = (x.cos() - &x).find_root_bracket(&x, 0.0, 1.0).unwrap();
assert!((r - 0.739_085_133_215_160_6).abs() < 1e-12);

// Another free symbol → FreeSymbol, not a silent NaN.
let a = ctx.symbol("a");
assert!(matches!(
    (&x.powi(2) - &a).find_root_bracket(&x, 0.0, 2.0),
    Err(SymplexError::FreeSymbol { .. })
));
Source

pub fn find_root_bracket_with( &self, var: &Ex, a: f64, b: f64, opts: &RootOpts, ) -> Result<f64, SymplexError>

find_root_bracket with explicit RootOpts.

§Examples
use symplex::optimize::RootOpts;
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
let opts = RootOpts { xtol: 1e-6, ..RootOpts::default() };
let r = (x.exp() - 3).find_root_bracket_with(&x, 0.0, 2.0, &opts).unwrap();
assert!((r - 3f64.ln()).abs() < 1e-6);
Source

pub fn minimize_numeric( &self, vars: &[&Ex], x0: &[f64], ) -> Result<MinimizeResult, SymplexError>

Minimise this expression numerically over vars from the starting point x0 by nelder_mead with default MinimizeOpts.

x0[i] is the initial value of vars[i].

§Errors
§Examples
use symplex::prelude::*;

let ctx = Context::new();
let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
let bowl = (&x - 1).powi(2) + (&y + 2).powi(2);
let r = bowl.minimize_numeric(&[&x, &y], &[0.0, 0.0]).unwrap();
assert!(r.converged);
assert!((r.x[0] - 1.0).abs() < 1e-6 && (r.x[1] + 2.0).abs() < 1e-6);
assert!(r.fun < 1e-12);
Source

pub fn minimize_numeric_with( &self, vars: &[&Ex], x0: &[f64], opts: &MinimizeOpts, ) -> Result<MinimizeResult, SymplexError>

minimize_numeric with explicit MinimizeOpts.

§Examples
use symplex::optimize::MinimizeOpts;
use symplex::prelude::*;

let ctx = Context::new();
let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
let rosen = (1 - &x).powi(2) + 100 * (&y - &x.powi(2)).powi(2);
let opts = MinimizeOpts { max_iter: 2000, ..MinimizeOpts::default() };
let r = rosen.minimize_numeric_with(&[&x, &y], &[-1.2, 1.0], &opts).unwrap();
assert!((r.x[0] - 1.0).abs() < 1e-4 && (r.x[1] - 1.0).abs() < 1e-4);
Source

pub fn minimize_scalar_numeric( &self, var: &Ex, a: f64, b: f64, ) -> Result<(f64, f64), SymplexError>

Minimise this expression in the single variable var over [a, b] by Brent’s method (minimize_scalar) with default MinimizeOpts. Returns (x_min, f_min).

§Errors

As for find_root_bracket plus the interval rules of minimize_scalar.

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
// x·ln x has its minimum −1/e at x = 1/e.
let (xm, fm) = (&x * x.ln()).minimize_scalar_numeric(&x, 0.1, 2.0).unwrap();
assert!((xm - (-1.0f64).exp()).abs() < 1e-6);
assert!((fm + (-1.0f64).exp()).abs() < 1e-12);
Source

pub fn minimize_global_numeric( &self, vars: &[&Ex], bounds: &[(f64, f64)], opts: &DeOpts, ) -> Result<MinimizeResult, SymplexError>

Globally minimise this expression over the box bounds (one (lo, hi) pair per entry of vars) by differential_evolution.

§Errors

As for minimize_numeric, with bounds.len() playing the role of x0.len(), plus the option and bound rules of differential_evolution.

§Examples
use symplex::optimize::DeOpts;
use symplex::prelude::*;

let ctx = Context::new();
let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
// Himmelblau's function has four global minima with f = 0.
let h = (&x.powi(2) + &y - 11).powi(2) + (&x + &y.powi(2) - 7).powi(2);
let r = h.minimize_global_numeric(&[&x, &y], &[(-5.0, 5.0), (-5.0, 5.0)], &DeOpts::default()).unwrap();
assert!(r.fun < 1e-8, "f = {}", r.fun);
Source

pub fn poly_fit_points( ctx: &Context, points: &[(Ex, Ex)], var: &Ex, degree: usize, ) -> Result<Ex, SymplexError>

Exact least-squares polynomial of degree degree in var through the rational points (x, y).

Each coordinate is constant-folded with eval and must then be a rational literal (ctx.int, ctx.rational, sqrt(4), …). The fit is computed by poly_fit_exact, so the result is the exact least-squares polynomial — the interpolating polynomial when degree + 1 == points.len() or the data are consistent.

§Errors
§Panics

Panics if var or a point belongs to a different context than ctx (the standard cross-context guard).

§Examples
use symplex::prelude::*;

let ctx = Context::new();
let x = ctx.symbol("x");
// Five samples of x²/3 − x/2 + 1/7.
let pts = [
    (ctx.int(0), ctx.rational(1, 7)),
    (ctx.int(1), ctx.rational(-1, 42)),
    (ctx.int(2), ctx.rational(10, 21)),
    (ctx.int(3), ctx.rational(23, 14)),
    (ctx.int(4), ctx.rational(73, 21)),
];
let p = Ex::poly_fit_points(&ctx, &pts, &x, 2).unwrap();
let expected = &x.powi(2) * ctx.rational(1, 3) - &x * ctx.rational(1, 2) + ctx.rational(1, 7);
assert!((&p - &expected).expand().is_zero_structural(), "{p}");

// A symbolic coordinate is rejected.
let a = ctx.symbol("a");
assert!(Ex::poly_fit_points(&ctx, &[(ctx.int(0), a), (ctx.int(1), ctx.int(1))], &x, 1).is_err());

Trait Implementations§

Source§

impl Add for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the + operator.
Source§

fn add(self, rhs: Ex) -> Ex

Performs the + operation. Read more
Source§

impl Add<&Expr<Numeric>> for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the + operator.
Source§

fn add(self, rhs: &Ex) -> Ex

Performs the + operation. Read more
Source§

impl Add<&Expr<Numeric>> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the + operator.
Source§

fn add(self, rhs: &Ex) -> Ex

Performs the + operation. Read more
Source§

impl Add<Expr<Numeric>> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the + operator.
Source§

fn add(self, rhs: Ex) -> Ex

Performs the + operation. Read more
Source§

impl<T: Scalar> Add<T> for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the + operator.
Source§

fn add(self, rhs: T) -> Ex

Performs the + operation. Read more
Source§

impl<T: Scalar> Add<T> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the + operator.
Source§

fn add(self, rhs: T) -> Ex

Performs the + operation. Read more
Source§

impl AddAssign for Ex

Source§

fn add_assign(&mut self, rhs: Ex)

Performs the += operation. Read more
Source§

impl AddAssign<&Expr<Numeric>> for Ex

Source§

fn add_assign(&mut self, rhs: &Ex)

Performs the += operation. Read more
Source§

impl<T: Scalar> AddAssign<T> for Ex

Source§

fn add_assign(&mut self, rhs: T)

Performs the += operation. Read more
Source§

impl AsRef<Expr<Numeric>> for Ex

Source§

fn as_ref(&self) -> &Ex

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl Div for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the / operator.
Source§

fn div(self, rhs: Ex) -> Ex

Performs the / operation. Read more
Source§

impl Div<&Expr<Numeric>> for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the / operator.
Source§

fn div(self, rhs: &Ex) -> Ex

Performs the / operation. Read more
Source§

impl Div<&Expr<Numeric>> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the / operator.
Source§

fn div(self, rhs: &Ex) -> Ex

Performs the / operation. Read more
Source§

impl Div<Expr<Numeric>> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the / operator.
Source§

fn div(self, rhs: Ex) -> Ex

Performs the / operation. Read more
Source§

impl<T: Scalar> Div<T> for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the / operator.
Source§

fn div(self, rhs: T) -> Ex

Performs the / operation. Read more
Source§

impl<T: Scalar> Div<T> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the / operator.
Source§

fn div(self, rhs: T) -> Ex

Performs the / operation. Read more
Source§

impl DivAssign for Ex

Source§

fn div_assign(&mut self, rhs: Ex)

Performs the /= operation. Read more
Source§

impl DivAssign<&Expr<Numeric>> for Ex

Source§

fn div_assign(&mut self, rhs: &Ex)

Performs the /= operation. Read more
Source§

impl<T: Scalar> DivAssign<T> for Ex

Source§

fn div_assign(&mut self, rhs: T)

Performs the /= operation. Read more
Source§

impl IntoEx for Ex

Source§

fn into_ex(self) -> Ex

Convert to an owned Ex.
Source§

impl IntoEx for &Ex

Source§

fn into_ex(self) -> Ex

Convert to an owned Ex.
Source§

impl Mul for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Ex) -> Ex

Performs the * operation. Read more
Source§

impl Mul<&Expr<Numeric>> for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: &Ex) -> Ex

Performs the * operation. Read more
Source§

impl Mul<&Expr<Numeric>> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: &Ex) -> Ex

Performs the * operation. Read more
Source§

impl Mul<&Matrix> for &Ex

Source§

type Output = Matrix

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: &Matrix) -> Matrix

Performs the * operation. Read more
Source§

impl Mul<&Matrix> for Ex

Source§

type Output = Matrix

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: &Matrix) -> Matrix

Performs the * operation. Read more
Source§

impl<D> Mul<&Qty<D>> for &Ex

Source§

type Output = Qty<D>

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: &Qty<D>) -> Qty<D>

Performs the * operation. Read more
Source§

impl Mul<&Quaternion> for &Ex

Source§

type Output = Quaternion

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: &Quaternion) -> Quaternion

Performs the * operation. Read more
Source§

impl Mul<&Quaternion> for Ex

Source§

type Output = Quaternion

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: &Quaternion) -> Quaternion

Performs the * operation. Read more
Source§

impl Mul<Acceleration> for &Ex

Source§

type Output = Acceleration

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Acceleration) -> Acceleration

Performs the * operation. Read more
Source§

impl Mul<Angle> for &Ex

Source§

type Output = Angle

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Angle) -> Angle

Performs the * operation. Read more
Source§

impl Mul<AngularAcceleration> for &Ex

Source§

type Output = AngularAcceleration

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: AngularAcceleration) -> AngularAcceleration

Performs the * operation. Read more
Source§

impl Mul<AngularMomentum> for &Ex

Source§

type Output = AngularMomentum

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: AngularMomentum) -> AngularMomentum

Performs the * operation. Read more
Source§

impl Mul<AngularVelocity> for &Ex

Source§

type Output = AngularVelocity

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: AngularVelocity) -> AngularVelocity

Performs the * operation. Read more
Source§

impl Mul<Area> for &Ex

Source§

type Output = Area

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Area) -> Area

Performs the * operation. Read more
Source§

impl Mul<Capacitance> for &Ex

Source§

type Output = Capacitance

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Capacitance) -> Capacitance

Performs the * operation. Read more
Source§

impl Mul<Charge> for &Ex

Source§

type Output = Charge

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Charge) -> Charge

Performs the * operation. Read more
Source§

impl Mul<Current> for &Ex

Source§

type Output = Current

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Current) -> Current

Performs the * operation. Read more
Source§

impl Mul<Damping> for &Ex

Source§

type Output = Damping

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Damping) -> Damping

Performs the * operation. Read more
Source§

impl Mul<Dimensionless> for &Ex

Source§

type Output = Dimensionless

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Dimensionless) -> Dimensionless

Performs the * operation. Read more
Source§

impl Mul<Energy> for &Ex

Source§

type Output = Energy

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Energy) -> Energy

Performs the * operation. Read more
Source§

impl Mul<Expr<Numeric>> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Ex) -> Ex

Performs the * operation. Read more
Source§

impl Mul<Force> for &Ex

Source§

type Output = Force

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Force) -> Force

Performs the * operation. Read more
Source§

impl Mul<Frequency> for &Ex

Source§

type Output = Frequency

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Frequency) -> Frequency

Performs the * operation. Read more
Source§

impl Mul<Inductance> for &Ex

Source§

type Output = Inductance

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Inductance) -> Inductance

Performs the * operation. Read more
Source§

impl Mul<Length> for &Ex

Source§

type Output = Length

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Length) -> Length

Performs the * operation. Read more
Source§

impl Mul<MagneticFlux> for &Ex

Source§

type Output = MagneticFlux

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: MagneticFlux) -> MagneticFlux

Performs the * operation. Read more
Source§

impl Mul<Mass> for &Ex

Source§

type Output = Mass

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Mass) -> Mass

Performs the * operation. Read more
Source§

impl Mul<Matrix> for &Ex

Source§

type Output = Matrix

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Matrix) -> Matrix

Performs the * operation. Read more
Source§

impl Mul<Matrix> for Ex

Source§

type Output = Matrix

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Matrix) -> Matrix

Performs the * operation. Read more
Source§

impl Mul<MomentOfInertia> for &Ex

Source§

type Output = MomentOfInertia

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: MomentOfInertia) -> MomentOfInertia

Performs the * operation. Read more
Source§

impl Mul<Momentum> for &Ex

Source§

type Output = Momentum

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Momentum) -> Momentum

Performs the * operation. Read more
Source§

impl Mul<Power> for &Ex

Source§

type Output = Power

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Power) -> Power

Performs the * operation. Read more
Source§

impl Mul<Pressure> for &Ex

Source§

type Output = Pressure

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Pressure) -> Pressure

Performs the * operation. Read more
Source§

impl<D> Mul<Qty<D>> for &Ex

Source§

type Output = Qty<D>

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Qty<D>) -> Qty<D>

Performs the * operation. Read more
Source§

impl Mul<Quaternion> for &Ex

Source§

type Output = Quaternion

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Quaternion) -> Quaternion

Performs the * operation. Read more
Source§

impl Mul<Quaternion> for Ex

Source§

type Output = Quaternion

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Quaternion) -> Quaternion

Performs the * operation. Read more
Source§

impl Mul<Resistance> for &Ex

Source§

type Output = Resistance

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Resistance) -> Resistance

Performs the * operation. Read more
Source§

impl Mul<Stiffness> for &Ex

Source§

type Output = Stiffness

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Stiffness) -> Stiffness

Performs the * operation. Read more
Source§

impl<T: Scalar> Mul<T> for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: T) -> Ex

Performs the * operation. Read more
Source§

impl<T: Scalar> Mul<T> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: T) -> Ex

Performs the * operation. Read more
Source§

impl Mul<Temperature> for &Ex

Source§

type Output = Temperature

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Temperature) -> Temperature

Performs the * operation. Read more
Source§

impl Mul<Time> for &Ex

Source§

type Output = Time

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Time) -> Time

Performs the * operation. Read more
Source§

impl Mul<Torque> for &Ex

Source§

type Output = Torque

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Torque) -> Torque

Performs the * operation. Read more
Source§

impl Mul<Velocity> for &Ex

Source§

type Output = Velocity

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Velocity) -> Velocity

Performs the * operation. Read more
Source§

impl Mul<Voltage> for &Ex

Source§

type Output = Voltage

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Voltage) -> Voltage

Performs the * operation. Read more
Source§

impl Mul<Volume> for &Ex

Source§

type Output = Volume

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Volume) -> Volume

Performs the * operation. Read more
Source§

impl MulAssign for Ex

Source§

fn mul_assign(&mut self, rhs: Ex)

Performs the *= operation. Read more
Source§

impl MulAssign<&Expr<Numeric>> for Ex

Source§

fn mul_assign(&mut self, rhs: &Ex)

Performs the *= operation. Read more
Source§

impl<T: Scalar> MulAssign<T> for Ex

Source§

fn mul_assign(&mut self, rhs: T)

Performs the *= operation. Read more
Source§

impl Neg for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the - operator.
Source§

fn neg(self) -> Ex

Performs the unary - operation. Read more
Source§

impl Neg for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the - operator.
Source§

fn neg(self) -> Ex

Performs the unary - operation. Read more
Source§

impl Product for Ex

Multiply an iterator of expressions.

§Panics

Panics on an empty iterator (no context to build 1 in) — see Context::product and the Option<Ex> implementation — and on mixed-context input.

Source§

fn product<I: Iterator<Item = Ex>>(iter: I) -> Self

Takes an iterator and generates Self from the elements by multiplying the items.
Source§

impl<'a> Product<&'a Expr<Numeric>> for Ex

Source§

fn product<I: Iterator<Item = &'a Ex>>(iter: I) -> Self

Takes an iterator and generates Self from the elements by multiplying the items.
Source§

impl Sub for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the - operator.
Source§

fn sub(self, rhs: Ex) -> Ex

Performs the - operation. Read more
Source§

impl Sub<&Expr<Numeric>> for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the - operator.
Source§

fn sub(self, rhs: &Ex) -> Ex

Performs the - operation. Read more
Source§

impl Sub<&Expr<Numeric>> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the - operator.
Source§

fn sub(self, rhs: &Ex) -> Ex

Performs the - operation. Read more
Source§

impl Sub<Expr<Numeric>> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the - operator.
Source§

fn sub(self, rhs: Ex) -> Ex

Performs the - operation. Read more
Source§

impl<T: Scalar> Sub<T> for Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the - operator.
Source§

fn sub(self, rhs: T) -> Ex

Performs the - operation. Read more
Source§

impl<T: Scalar> Sub<T> for &Ex

Source§

type Output = Expr<Numeric>

The resulting type after applying the - operator.
Source§

fn sub(self, rhs: T) -> Ex

Performs the - operation. Read more
Source§

impl SubAssign for Ex

Source§

fn sub_assign(&mut self, rhs: Ex)

Performs the -= operation. Read more
Source§

impl SubAssign<&Expr<Numeric>> for Ex

Source§

fn sub_assign(&mut self, rhs: &Ex)

Performs the -= operation. Read more
Source§

impl<T: Scalar> SubAssign<T> for Ex

Source§

fn sub_assign(&mut self, rhs: T)

Performs the -= operation. Read more
Source§

impl Sum for Ex

Sum an iterator of expressions.

§Panics

Panics on an empty iterator: there is no context in which to build 0. Use Context::sum (yields 0 on empty) or collect into Option<Ex> (yields None on empty) when the iterator may be empty. Also panics if the expressions come from different contexts.

use symplex::prelude::*;

let ctx = Context::new();
let total: Ex = (1..=4).map(|n| ctx.int(n)).sum();
assert_eq!(format!("{total}"), "10");

let none: Option<Ex> = std::iter::empty::<Ex>().sum();
assert!(none.is_none());
Source§

fn sum<I: Iterator<Item = Ex>>(iter: I) -> Self

Takes an iterator and generates Self from the elements by “summing up” the items.
Source§

impl<'a> Sum<&'a Expr<Numeric>> for Ex

Source§

fn sum<I: Iterator<Item = &'a Ex>>(iter: I) -> Self

Takes an iterator and generates Self from the elements by “summing up” the items.
Source§

impl ToEx for Ex

Source§

fn to_ex(&self, ctx: &Context) -> Ex

Build the exact expression for this value in ctx. Read more
Source§

impl ToEx for &Ex

Source§

fn to_ex(&self, ctx: &Context) -> Ex

Build the exact expression for this value in ctx. Read more
Source§

impl ZeroForm for Ex

Source§

fn to_zero_form(&self) -> Ex

Return the expression that equals zero when the equation holds.