|
8 | 8 | import com.oracle.graal.python.builtins.Builtin;
|
9 | 9 | import com.oracle.graal.python.builtins.CoreFunctions;
|
10 | 10 | import com.oracle.graal.python.builtins.PythonBuiltins;
|
| 11 | +import com.oracle.graal.python.builtins.objects.PNone; |
11 | 12 | import com.oracle.graal.python.builtins.objects.complex.ComplexBuiltins;
|
12 | 13 | import com.oracle.graal.python.builtins.objects.complex.PComplex;
|
13 | 14 | import com.oracle.graal.python.builtins.objects.tuple.PTuple;
|
@@ -39,6 +40,13 @@ public class CmathModuleBuiltins extends PythonBuiltins {
|
39 | 40 | // Constants used for the definition of special values tables in node classes
|
40 | 41 | static final double INF = Double.POSITIVE_INFINITY;
|
41 | 42 | static final double NAN = Double.NaN;
|
| 43 | + static final double P = Math.PI; |
| 44 | + static final double P14 = 0.25 * Math.PI; |
| 45 | + static final double P12 = 0.5 * Math.PI; |
| 46 | + static final double P34 = 0.75 * Math.PI; |
| 47 | + |
| 48 | + static final double largeDouble = Double.MAX_VALUE / 4.0; // used to avoid overflow |
| 49 | + static final double ln2 = 0.6931471805599453094; // natural log of 2 |
42 | 50 |
|
43 | 51 | @Override
|
44 | 52 | protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
|
@@ -346,6 +354,83 @@ private PComplex rect(double r, double phi) {
|
346 | 354 | }
|
347 | 355 | }
|
348 | 356 |
|
| 357 | + @Builtin(name = "log", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2) |
| 358 | + @TypeSystemReference(PythonArithmeticTypes.class) |
| 359 | + @ImportStatic(MathGuards.class) |
| 360 | + @GenerateNodeFactory |
| 361 | + abstract static class LogNode extends PythonBinaryBuiltinNode { |
| 362 | + |
| 363 | + // @formatter:off |
| 364 | + @CompilerDirectives.CompilationFinal(dimensions = 2) |
| 365 | + private static final ComplexConstant[][] SPECIAL_VALUES = { |
| 366 | + {C(INF, -P34), C(INF, -P), C(INF, -P), C(INF, P), C(INF, P), C(INF, P34), C(INF, NAN)}, |
| 367 | + {C(INF, -P12), null, null, null, null, C(INF, P12), C(NAN, NAN)}, |
| 368 | + {C(INF, -P12), null, C(-INF, -P), C(-INF, P), null, C(INF, P12), C(NAN, NAN)}, |
| 369 | + {C(INF, -P12), null, C(-INF, -0.0), C(-INF, 0.0), null, C(INF, P12), C(NAN, NAN)}, |
| 370 | + {C(INF, -P12), null, null, null, null, C(INF, P12), C(NAN, NAN)}, |
| 371 | + {C(INF, -P14), C(INF, -0.0), C(INF, -0.0), C(INF, 0.0), C(INF, 0.0), C(INF, P14), C(INF, NAN)}, |
| 372 | + {C(INF, NAN), C(NAN, NAN), C(NAN, NAN), C(NAN, NAN), C(NAN, NAN), C(INF, NAN), C(NAN, NAN)}, |
| 373 | + }; |
| 374 | + // @formatter:on |
| 375 | + |
| 376 | + @Specialization(guards = "isNoValue(y)") |
| 377 | + PComplex doComplexNone(PComplex x, @SuppressWarnings("unused") PNone y) { |
| 378 | + return log(x); |
| 379 | + } |
| 380 | + |
| 381 | + @Specialization |
| 382 | + PComplex doComplexComplex(VirtualFrame frame, PComplex x, PComplex y, @Cached ComplexBuiltins.DivNode divNode) { |
| 383 | + return divNode.executeComplex(frame, log(x), log(y)); |
| 384 | + } |
| 385 | + |
| 386 | + @Specialization(guards = "isNoValue(yObj)") |
| 387 | + PComplex doGeneral(VirtualFrame frame, Object xObj, @SuppressWarnings("unused") PNone yObj, |
| 388 | + @Cached CoerceToComplexNode coerceXToComplex) { |
| 389 | + return log(coerceXToComplex.execute(frame, xObj)); |
| 390 | + } |
| 391 | + |
| 392 | + @Specialization(guards = "!isNoValue(yObj)") |
| 393 | + PComplex doGeneral(VirtualFrame frame, Object xObj, Object yObj, @Cached CoerceToComplexNode coerceXToComplex, |
| 394 | + @Cached CoerceToComplexNode coerceYToComplex, @Cached ComplexBuiltins.DivNode divNode) { |
| 395 | + PComplex x = log(coerceXToComplex.execute(frame, xObj)); |
| 396 | + PComplex y = log(coerceYToComplex.execute(frame, yObj)); |
| 397 | + return divNode.executeComplex(frame, x, y); |
| 398 | + } |
| 399 | + |
| 400 | + private PComplex log(PComplex z) { |
| 401 | + PComplex r = specialValue(factory(), SPECIAL_VALUES, z.getReal(), z.getImag()); |
| 402 | + if (r != null) { |
| 403 | + return r; |
| 404 | + } |
| 405 | + double real = computeRealPart(z.getReal(), z.getImag()); |
| 406 | + double imag = Math.atan2(z.getImag(), z.getReal()); |
| 407 | + return factory().createComplex(real, imag); |
| 408 | + } |
| 409 | + |
| 410 | + private double computeRealPart(double real, double imag) { |
| 411 | + double ax = Math.abs(real); |
| 412 | + double ay = Math.abs(imag); |
| 413 | + |
| 414 | + if (ax > largeDouble || ay > largeDouble) { |
| 415 | + return Math.log(Math.hypot(ax / 2.0, ay / 2.0)) + ln2; |
| 416 | + } |
| 417 | + if (ax < Double.MIN_NORMAL && ay < Double.MIN_NORMAL) { |
| 418 | + if (ax > 0.0 || ay > 0.0) { |
| 419 | + final double scaleUp = 0x1.0p53; |
| 420 | + return Math.log(Math.hypot(ax * scaleUp, ay * scaleUp)) - 53 * ln2; |
| 421 | + } |
| 422 | + throw raise(ValueError, "math domain error"); |
| 423 | + } |
| 424 | + double h = Math.hypot(ax, ay); |
| 425 | + if (0.71 <= h && h <= 1.73) { |
| 426 | + double am = Math.max(ax, ay); |
| 427 | + double an = Math.min(ax, ay); |
| 428 | + return Math.log1p((am - 1) * (am + 1) + an * an) / 2.0; |
| 429 | + } |
| 430 | + return Math.log(h); |
| 431 | + } |
| 432 | + } |
| 433 | + |
349 | 434 | @Builtin(name = "sqrt", minNumOfPositionalArgs = 1)
|
350 | 435 | @GenerateNodeFactory
|
351 | 436 | abstract static class SqrtNode extends CmathComplexUnaryBuiltinNode {
|
|
0 commit comments