Skip to content

Commit

Permalink
[Python] fixes for extreme math values (#3072)
Browse files Browse the repository at this point in the history
* [Python] fixes for math extreme values

* fix: random differences
  • Loading branch information
dbrattli authored Aug 14, 2022
1 parent 3199a59 commit df4bedd
Show file tree
Hide file tree
Showing 32 changed files with 433 additions and 43 deletions.
25 changes: 23 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ python-dateutil = "^2.8.2"
fable-library = {path = "./build/fable-library-py", develop = true}
pytest = "^6.2.4"
black = {version = "^22.6.0", allow-prereleases = true}
isort = "^5.10.1"

[tool.pyright]
reportMissingTypeStubs = false
Expand All @@ -36,6 +37,17 @@ reportImplicitStringConcatenation = true
pythonVersion = "3.10"
typeCheckingMode = "strict"

[tool.isort]
profile = "black"
atomic = true
lines_after_imports = 2
lines_between_types = 1
multi_line_output = 3 # corresponds to -m flag
include_trailing_comma = true # corresponds to -tc flag
line_length = 88
known_third_party = ["cognite","pytest"]
py_version=310

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
9 changes: 4 additions & 5 deletions src/Fable.Transforms/Python/Fable2Python.fs
Original file line number Diff line number Diff line change
Expand Up @@ -527,11 +527,8 @@ module Helpers =

/// Replaces all '$' and `.`with '_'
let clean (name: string) =
match name with
| "Infinity" -> "float('inf')"
| _ ->
(name, Naming.NoMemberPart)
||> Naming.sanitizeIdent (fun _ -> false)
(name, Naming.NoMemberPart)
||> Naming.sanitizeIdent (fun _ -> false)

let unzipArgs (args: (Expression * Statement list) list) : Expression list * Statement list =
let stmts = args |> List.map snd |> List.collect id
Expand Down Expand Up @@ -1579,6 +1576,8 @@ module Util =
| _, (:? uint32 as x) -> makeNumber com ctx r value.Type "uint32" x
| _, x when x = infinity -> Expression.name "float('inf')", []
| _, x when x = -infinity -> Expression.name "float('-inf')", []
| _, (:? float as x) when Double.IsNaN(x) -> Expression.name "float('nan')", []
| _, (:? float32 as x) when Single.IsNaN(x) -> libCall com ctx r "types" "float32" [ Expression.constant "nan"], []
| _, (:? float32 as x) -> makeNumber com ctx r value.Type "float32" x
| _, (:? float as x) -> Expression.constant (x, ?loc = r), []
| _ -> Expression.constant (x, ?loc = r), []
Expand Down
7 changes: 5 additions & 2 deletions src/Fable.Transforms/Python/PythonPrinter.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// fsharplint:disable InterfaceNames
module Fable.Transforms.PythonPrinter

open System

open Fable
open Fable.AST
open Fable.AST.Python
Expand Down Expand Up @@ -562,8 +564,9 @@ module PrinterExtensions =
| :? float as value ->
let value = string value
printer.Print(value)
// Print with at least one decimal place
if value.IndexOf(".") = -1 && not (value.Contains("E")) then

// Make sure it's a valid Python float (not int)
if String.forall (fun char -> char = '-' || Char.IsDigit char) value then
printer.Print(".0")
| :? bool as value -> printer.Print(if value then "True" else "False")
| _ -> printer.Print(string value)
Expand Down
30 changes: 18 additions & 12 deletions src/Fable.Transforms/Python/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,9 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) =
[ left; right ] ->
match argTypes with
// Floor result of integer divisions (see #172)
| Number((Int8 | Int16 | Int32 | UInt8 | UInt16 | UInt32 | Int64 | UInt64 | BigInt),_) :: _ -> binOp BinaryDivide left right |> fastIntFloor
| _ -> binOp BinaryDivide left right
| Number((Int8 | Int16 | Int32 | UInt8 | UInt16 | UInt32 | Int64 | UInt64 | BigInt),_) :: _ ->
binOp BinaryDivide left right |> fastIntFloor
| _ -> Helper.LibCall(com, "double", "divide", t, [ left; right ], argTypes, ?loc=r)
| Operators.modulus, [ left; right ] -> binOp BinaryModulus left right
| Operators.leftShift, [ left; right ] ->
binOp BinaryShiftLeft left right
Expand Down Expand Up @@ -1446,15 +1447,17 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
| "Cos", _
| "Cosh", _
| "Exp", _
| "Log", _
| "Log10", _
| "Sin", _
| "Sinh", _
| "Sqrt", _
| "Tan", _
| "Tanh", _ ->
math r t args i.SignatureArgTypes i.CompiledName
|> Some
| "Log", _
| "Sqrt", _ ->
Helper.LibCall(com, "double", i.CompiledName.ToLower(), t, args, i.SignatureArgTypes, ?thisArg = thisArg, ?loc = r)
|> Some
| "Round", _ ->
match args with
| ExprType(Number(Decimal,_))::_ ->
Expand All @@ -1469,7 +1472,7 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
Helper.LibCall(com, "decimal", "truncate", t, args, i.SignatureArgTypes, ?thisArg = thisArg, ?loc = r)
|> Some
| _ ->
Helper.GlobalCall("math", t, args, i.SignatureArgTypes, memb = "trunc", ?loc = r)
Helper.ImportedCall("math", "trunc", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "Sign", _ ->
let args = toFloat com ctx r t args |> List.singleton
Expand All @@ -1480,12 +1483,12 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
| ("Infinity"
| "InfinitySingle"),
_ ->
Helper.GlobalIdent("Number", "POSITIVE_INFINITY", t, ?loc = r)
Helper.ImportedValue(com, "math", "inf", t)
|> Some
| ("NaN"
| "NaNSingle"),
_ ->
Helper.GlobalIdent("Number", "NaN", t, ?loc = r)
Helper.ImportedValue(com, "math", "nan", t)
|> Some
| "Fst", [ tup ] -> Get(tup, TupleIndex 0, t, r) |> Some
| "Snd", [ tup ] -> Get(tup, TupleIndex 1, t, r) |> Some
Expand Down Expand Up @@ -2402,10 +2405,13 @@ let parseNum (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr op

match i.CompiledName, args with
| "IsNaN", [ _ ] when isFloat ->
Helper.GlobalCall("Number", t, args, memb = "isNaN", ?loc = r)
Helper.ImportedCall("math", "isnan", t, args, ?loc = r)
|> Some
| "IsInfinity", [ _ ] when isFloat ->
Helper.LibCall(com, "double", "isInfinity", t, args, i.SignatureArgTypes, ?loc = r)
Helper.ImportedCall("math", "isinf", t, args, ?loc = r)
|> Some
| "IsNegativeInfinity" , [ _ ] when isFloat ->
Helper.LibCall(com, "double", "is_negative_inf", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| ("Parse"
| "TryParse") as meth,
Expand Down Expand Up @@ -2726,7 +2732,7 @@ let intrinsicFunctions (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisAr
// Type: PowDouble : float -> int -> float
// Usage: PowDouble x n
| "PowDouble", None, _ ->
Helper.GlobalCall("math", t, args, i.SignatureArgTypes, memb = "pow", ?loc = r)
Helper.ImportedCall("math", "pow", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "PowDecimal", None, _ ->
Helper.LibCall(com, "decimal", "pow", t, args, i.SignatureArgTypes, ?loc = r)
Expand Down Expand Up @@ -3274,10 +3280,10 @@ let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (_: Expr option) (a
| [ min; max ] -> min, max
| _ -> FableError "Unexpected arg count for Random.Next" |> raise

Helper.LibCall(com, "util", "randomNext", t, [ min; max ], [ min.Type; max.Type ], ?loc = r)
Helper.LibCall(com, "util", "randint", t, [ min; max ], [ min.Type; max.Type ], ?loc = r)
|> Some
| "NextDouble" ->
Helper.GlobalCall("math", t, [], [], memb = "random")
Helper.ImportedCall("random", "random", t, [], [])
|> Some
| "NextBytes" ->
let byteArray =
Expand Down
3 changes: 3 additions & 0 deletions src/Fable.Transforms/Replacements.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ type Helper =
let info = CallInfo.Create(?thisArg=thisArg, args=args, ?sigArgTypes=argTypes, ?genArgs=genArgs, ?memberRef=memberRef, ?isCons=isConstructor)
Call(callee, info, returnType, loc)

static member ImportedValue(com, coreModule: string, coreMember: string, returnType: Type) =
makeImportUserGenerated None Any coreMember coreModule

static member ImportedCall(path: string, selector: string, returnType: Type, args: Expr list,
?argTypes: Type list, ?genArgs, ?thisArg: Expr, ?hasSpread: bool, ?isConstructor: bool, ?loc: SourceLocation) =
let callee = makeImportUserGenerated None Any selector path
Expand Down
2 changes: 2 additions & 0 deletions src/fable-library-py/fable_library/async_.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio

from asyncio import Future, ensure_future
from concurrent.futures import ThreadPoolExecutor
from threading import Timer
Expand All @@ -21,6 +22,7 @@
from .choice import Choice_makeChoice2Of2 # type: ignore
from .task import TaskCompletionSource


_T = TypeVar("_T")


Expand Down
1 change: 1 addition & 0 deletions src/fable-library-py/fable_library/async_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from .util import IDisposable


_T = TypeVar("_T")
_U = TypeVar("_U")
_D = TypeVar("_D", bound=IDisposable)
Expand Down
12 changes: 10 additions & 2 deletions src/fable-library-py/fable_library/big_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,22 @@ def equals(a: int, b: int) -> bool:
return a == b


def try_parse(string: str, defValue: FSharpRef[int]) -> bool:
def try_parse(string: str, def_value: FSharpRef[int]) -> bool:
try:
defValue.contents = parse(string)
def_value.contents = parse(string)
return True
except Exception:
return False


def op_right_shift(a: int, num_bits: int) -> int:
return a >> num_bits


def of_left_shift(a: int, num_bits: int) -> int:
return a << num_bits


BigInteger = int

__all__ = [
Expand Down
1 change: 1 addition & 0 deletions src/fable-library-py/fable_library/bit_converter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import struct
import sys

from typing import Optional


Expand Down
1 change: 1 addition & 0 deletions src/fable-library-py/fable_library/char.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unicodedata

from enum import IntEnum
from typing import Dict, Union

Expand Down
2 changes: 2 additions & 0 deletions src/fable-library-py/fable_library/date.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import re

from datetime import datetime, timedelta, timezone
from typing import Any, Match, Optional

from .types import FSharpRef
from .util import DateKind


formatRegExp = re.compile(r"(\w)\1*")


Expand Down
7 changes: 7 additions & 0 deletions src/fable-library-py/fable_library/decimal.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import builtins

from decimal import MAX_EMAX, MIN_EMIN, Decimal, getcontext

from .types import FSharpRef


getcontext().prec = 29

get_zero = Decimal(0)
Expand Down Expand Up @@ -67,6 +70,10 @@ def equals(a: Decimal, b: Decimal) -> bool:
return a == b


def abs(a: Decimal) -> Decimal:
return builtins.abs(a)


__all__ = [
"equals",
"try_parse",
Expand Down
35 changes: 35 additions & 0 deletions src/fable-library-py/fable_library/double.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
import math

from math import copysign
from typing import Any

from .types import FSharpRef


def divide(x: float, y: float) -> float:
"""Divide two numbers.
Returns nan if y is 0.
"""
if y == 0:
if x == 0:
return float("nan")
return float("inf") if copysign(1, x) == copysign(1, y) else float("-inf")

return x / y


def log(x: float) -> float:
if x == 0:
return float("-inf")
elif x < 0:
return float("nan")
return math.log(x)


def sqrt(x: float) -> float:
try:
return math.sqrt(x)
except ValueError:
return float("nan")


def is_negative_inf(value: float) -> bool:
return math.isinf(value) and value < 0


def parse(value: Any) -> float:
try:
return float(value)
Expand Down
12 changes: 7 additions & 5 deletions src/fable-library-py/fable_library/event.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
from abc import abstractmethod
import inspect

from abc import abstractmethod
from typing import (
Any,
Callable,
Generic,
List,
Optional,
Protocol,
TypeVar,
Union,
Generic,
overload,
)

from .observable import IObservable, IObserver, Observer
from .option import Option, some, value
from .choice import (
FSharpChoice_2,
Choice_tryValueIfChoice1Of2,
Choice_tryValueIfChoice2Of2,
FSharpChoice_2,
)
from .observable import IObservable, IObserver, Observer
from .option import Option, some, value
from .util import IDisposable


_T = TypeVar("_T")
_U = TypeVar("_U")
_V = TypeVar("_V")
Expand Down
Loading

0 comments on commit df4bedd

Please sign in to comment.