Skip to content

Commit ace210d

Browse files
committed
pymath 0.1.5
1 parent 1459dfe commit ace210d

File tree

7 files changed

+698
-834
lines changed

7 files changed

+698
-834
lines changed

Cargo.lock

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ members = [
116116
".",
117117
"crates/*",
118118
]
119+
exclude = ["pymath"]
119120

120121
[workspace.package]
121122
version = "0.4.0"
@@ -184,7 +185,7 @@ once_cell = "1.20.3"
184185
parking_lot = "0.12.3"
185186
paste = "1.0.15"
186187
proc-macro2 = "1.0.105"
187-
pymath = "0.0.2"
188+
pymath = { version = "0.1.5", features = ["mul_add", "malachite-bigint", "complex"] }
188189
quote = "1.0.43"
189190
radium = "1.1.1"
190191
rand = "0.9"

Lib/test/test_cmath.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -529,13 +529,11 @@ def testTanhSign(self):
529529
# log1p function; If that system function doesn't respect the sign
530530
# of zero, then atan and atanh will also have difficulties with
531531
# the sign of complex zeros.
532-
@unittest.expectedFailure # TODO: RUSTPYTHON
533532
@requires_IEEE_754
534533
def testAtanSign(self):
535534
for z in complex_zeros:
536535
self.assertComplexesAreIdentical(cmath.atan(z), z)
537536

538-
@unittest.expectedFailure # TODO: RUSTPYTHON
539537
@requires_IEEE_754
540538
def testAtanhSign(self):
541539
for z in complex_zeros:
@@ -582,7 +580,6 @@ def test_complex_near_zero(self):
582580
self.assertIsClose(0.001-0.001j, 0.001+0.001j, abs_tol=2e-03)
583581
self.assertIsNotClose(0.001-0.001j, 0.001+0.001j, abs_tol=1e-03)
584582

585-
@unittest.expectedFailure # TODO: RUSTPYTHON
586583
def test_complex_special(self):
587584
self.assertIsNotClose(INF, INF*1j)
588585
self.assertIsNotClose(INF*1j, INF)

Lib/test/test_math.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2038,7 +2038,6 @@ def test_exceptions(self):
20382038
else:
20392039
self.fail("sqrt(-1) didn't raise ValueError")
20402040

2041-
@unittest.expectedFailure # TODO: RUSTPYTHON
20422041
@requires_IEEE_754
20432042
def test_testfile(self):
20442043
# Some tests need to be skipped on ancient OS X versions.

crates/stdlib/src/cmath.rs

Lines changed: 65 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
// TODO: Keep track of rust-num/num-complex/issues/2. A common trait could help with duplication
2-
// that exists between cmath and math.
31
pub(crate) use cmath::make_module;
2+
43
#[pymodule]
54
mod cmath {
65
use crate::vm::{
@@ -9,137 +8,141 @@ mod cmath {
98
};
109
use num_complex::Complex64;
1110

11+
use crate::math::pymath_exception;
12+
1213
// Constants
13-
#[pyattr]
14-
use core::f64::consts::{E as e, PI as pi, TAU as tau};
14+
#[pyattr(name = "e")]
15+
const E: f64 = pymath::cmath::E;
16+
#[pyattr(name = "pi")]
17+
const PI: f64 = pymath::cmath::PI;
18+
#[pyattr(name = "tau")]
19+
const TAU: f64 = pymath::cmath::TAU;
1520
#[pyattr(name = "inf")]
16-
const INF: f64 = f64::INFINITY;
21+
const INF: f64 = pymath::cmath::INF;
1722
#[pyattr(name = "nan")]
18-
const NAN: f64 = f64::NAN;
23+
const NAN: f64 = pymath::cmath::NAN;
1924
#[pyattr(name = "infj")]
20-
const INFJ: Complex64 = Complex64::new(0., f64::INFINITY);
25+
const INFJ: Complex64 = pymath::cmath::INFJ;
2126
#[pyattr(name = "nanj")]
22-
const NANJ: Complex64 = Complex64::new(0., f64::NAN);
27+
const NANJ: Complex64 = pymath::cmath::NANJ;
2328

2429
#[pyfunction]
25-
fn phase(z: ArgIntoComplex) -> f64 {
26-
z.into_complex().arg()
30+
fn phase(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<f64> {
31+
pymath::cmath::phase(z.into_complex()).map_err(|err| pymath_exception(err, vm))
2732
}
2833

2934
#[pyfunction]
30-
fn polar(x: ArgIntoComplex) -> (f64, f64) {
31-
x.into_complex().to_polar()
35+
fn polar(x: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<(f64, f64)> {
36+
pymath::cmath::polar(x.into_complex()).map_err(|err| pymath_exception(err, vm))
3237
}
3338

3439
#[pyfunction]
35-
fn rect(r: ArgIntoFloat, phi: ArgIntoFloat) -> Complex64 {
36-
Complex64::from_polar(r.into_float(), phi.into_float())
40+
fn rect(r: ArgIntoFloat, phi: ArgIntoFloat, vm: &VirtualMachine) -> PyResult<Complex64> {
41+
pymath::cmath::rect(r.into_float(), phi.into_float())
42+
.map_err(|err| pymath_exception(err, vm))
3743
}
3844

3945
#[pyfunction]
4046
fn isinf(z: ArgIntoComplex) -> bool {
41-
let Complex64 { re, im } = z.into_complex();
42-
re.is_infinite() || im.is_infinite()
47+
pymath::cmath::isinf(z.into_complex())
4348
}
4449

4550
#[pyfunction]
4651
fn isfinite(z: ArgIntoComplex) -> bool {
47-
z.into_complex().is_finite()
52+
pymath::cmath::isfinite(z.into_complex())
4853
}
4954

5055
#[pyfunction]
5156
fn isnan(z: ArgIntoComplex) -> bool {
52-
z.into_complex().is_nan()
57+
pymath::cmath::isnan(z.into_complex())
5358
}
5459

5560
#[pyfunction]
5661
fn exp(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
57-
let z = z.into_complex();
58-
result_or_overflow(z, z.exp(), vm)
62+
pymath::cmath::exp(z.into_complex()).map_err(|err| pymath_exception(err, vm))
5963
}
6064

6165
#[pyfunction]
62-
fn sqrt(z: ArgIntoComplex) -> Complex64 {
63-
z.into_complex().sqrt()
66+
fn sqrt(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
67+
pymath::cmath::sqrt(z.into_complex()).map_err(|err| pymath_exception(err, vm))
6468
}
6569

6670
#[pyfunction]
67-
fn sin(z: ArgIntoComplex) -> Complex64 {
68-
z.into_complex().sin()
71+
fn sin(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
72+
pymath::cmath::sin(z.into_complex()).map_err(|err| pymath_exception(err, vm))
6973
}
7074

7175
#[pyfunction]
72-
fn asin(z: ArgIntoComplex) -> Complex64 {
73-
z.into_complex().asin()
76+
fn asin(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
77+
pymath::cmath::asin(z.into_complex()).map_err(|err| pymath_exception(err, vm))
7478
}
7579

7680
#[pyfunction]
77-
fn cos(z: ArgIntoComplex) -> Complex64 {
78-
z.into_complex().cos()
81+
fn cos(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
82+
pymath::cmath::cos(z.into_complex()).map_err(|err| pymath_exception(err, vm))
7983
}
8084

8185
#[pyfunction]
82-
fn acos(z: ArgIntoComplex) -> Complex64 {
83-
z.into_complex().acos()
86+
fn acos(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
87+
pymath::cmath::acos(z.into_complex()).map_err(|err| pymath_exception(err, vm))
8488
}
8589

8690
#[pyfunction]
87-
fn log(z: ArgIntoComplex, base: OptionalArg<ArgIntoComplex>) -> Complex64 {
88-
// TODO: Complex64.log with a negative base yields wrong results.
89-
// Issue is with num_complex::Complex64 implementation of log
90-
// which returns NaN when base is negative.
91-
// log10(z) / log10(base) yields correct results but division
92-
// doesn't handle pos/neg zero nicely. (i.e log(1, 0.5))
93-
z.into_complex().log(
94-
base.into_option()
95-
.map(|base| base.into_complex().re)
96-
.unwrap_or(core::f64::consts::E),
91+
fn log(
92+
z: ArgIntoComplex,
93+
base: OptionalArg<ArgIntoComplex>,
94+
vm: &VirtualMachine,
95+
) -> PyResult<Complex64> {
96+
pymath::cmath::log(
97+
z.into_complex(),
98+
base.into_option().map(|b| b.into_complex()),
9799
)
100+
.map_err(|err| pymath_exception(err, vm))
98101
}
99102

100103
#[pyfunction]
101-
fn log10(z: ArgIntoComplex) -> Complex64 {
102-
z.into_complex().log(10.0)
104+
fn log10(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
105+
pymath::cmath::log10(z.into_complex()).map_err(|err| pymath_exception(err, vm))
103106
}
104107

105108
#[pyfunction]
106-
fn acosh(z: ArgIntoComplex) -> Complex64 {
107-
z.into_complex().acosh()
109+
fn acosh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
110+
pymath::cmath::acosh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
108111
}
109112

110113
#[pyfunction]
111-
fn atan(z: ArgIntoComplex) -> Complex64 {
112-
z.into_complex().atan()
114+
fn atan(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
115+
pymath::cmath::atan(z.into_complex()).map_err(|err| pymath_exception(err, vm))
113116
}
114117

115118
#[pyfunction]
116-
fn atanh(z: ArgIntoComplex) -> Complex64 {
117-
z.into_complex().atanh()
119+
fn atanh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
120+
pymath::cmath::atanh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
118121
}
119122

120123
#[pyfunction]
121-
fn tan(z: ArgIntoComplex) -> Complex64 {
122-
z.into_complex().tan()
124+
fn tan(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
125+
pymath::cmath::tan(z.into_complex()).map_err(|err| pymath_exception(err, vm))
123126
}
124127

125128
#[pyfunction]
126-
fn tanh(z: ArgIntoComplex) -> Complex64 {
127-
z.into_complex().tanh()
129+
fn tanh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
130+
pymath::cmath::tanh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
128131
}
129132

130133
#[pyfunction]
131-
fn sinh(z: ArgIntoComplex) -> Complex64 {
132-
z.into_complex().sinh()
134+
fn sinh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
135+
pymath::cmath::sinh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
133136
}
134137

135138
#[pyfunction]
136-
fn cosh(z: ArgIntoComplex) -> Complex64 {
137-
z.into_complex().cosh()
139+
fn cosh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
140+
pymath::cmath::cosh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
138141
}
139142

140143
#[pyfunction]
141-
fn asinh(z: ArgIntoComplex) -> Complex64 {
142-
z.into_complex().asinh()
144+
fn asinh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
145+
pymath::cmath::asinh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
143146
}
144147

145148
#[derive(FromArgs)]
@@ -158,52 +161,10 @@ mod cmath {
158161
fn isclose(args: IsCloseArgs, vm: &VirtualMachine) -> PyResult<bool> {
159162
let a = args.a.into_complex();
160163
let b = args.b.into_complex();
161-
let rel_tol = args.rel_tol.map_or(1e-09, |v| v.into_float());
162-
let abs_tol = args.abs_tol.map_or(0.0, |v| v.into_float());
163-
164-
if rel_tol < 0.0 || abs_tol < 0.0 {
165-
return Err(vm.new_value_error("tolerances must be non-negative"));
166-
}
167-
168-
if a == b {
169-
/* short circuit exact equality -- needed to catch two infinities of
170-
the same sign. And perhaps speeds things up a bit sometimes.
171-
*/
172-
return Ok(true);
173-
}
174-
175-
/* This catches the case of two infinities of opposite sign, or
176-
one infinity and one finite number. Two infinities of opposite
177-
sign would otherwise have an infinite relative tolerance.
178-
Two infinities of the same sign are caught by the equality check
179-
above.
180-
*/
181-
if a.is_infinite() || b.is_infinite() {
182-
return Ok(false);
183-
}
164+
let rel_tol = args.rel_tol.into_option().map(|v| v.into_float());
165+
let abs_tol = args.abs_tol.into_option().map(|v| v.into_float());
184166

185-
let diff = c_abs(b - a);
186-
187-
Ok(diff <= (rel_tol * c_abs(b)) || (diff <= (rel_tol * c_abs(a))) || diff <= abs_tol)
188-
}
189-
190-
#[inline]
191-
fn c_abs(Complex64 { re, im }: Complex64) -> f64 {
192-
re.hypot(im)
193-
}
194-
195-
#[inline]
196-
fn result_or_overflow(
197-
value: Complex64,
198-
result: Complex64,
199-
vm: &VirtualMachine,
200-
) -> PyResult<Complex64> {
201-
if !result.is_finite() && value.is_finite() {
202-
// CPython doesn't return `inf` when called with finite
203-
// values, it raises OverflowError instead.
204-
Err(vm.new_overflow_error("math range error"))
205-
} else {
206-
Ok(result)
207-
}
167+
pymath::cmath::isclose(a, b, rel_tol, abs_tol)
168+
.map_err(|_| vm.new_value_error("tolerances must be non-negative"))
208169
}
209170
}

0 commit comments

Comments
 (0)