Skip to content

Commit

Permalink
Add Warnings whenever a floating-point value is used as a Dictionary …
Browse files Browse the repository at this point in the history
…key (#11417)
  • Loading branch information
GregoryTravis authored Nov 14, 2024
1 parent d0f3d7f commit 159a7a3
Show file tree
Hide file tree
Showing 30 changed files with 132 additions and 52 deletions.
18 changes: 15 additions & 3 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import project.Any.Any
import project.Data.Numbers.Float
import project.Data.Numbers.Integer
import project.Data.Pair.Pair
import project.Data.Text.Text
Expand All @@ -11,8 +12,10 @@ import project.Meta
import project.Metadata.Widget
import project.Nothing.Nothing
import project.Panic.Panic
import project.Warning.Warning
from project.Data.Boolean import Boolean, False, True
from project.Data.Text.Extensions import all
from project.Errors.Common import Floating_Point_Equality
from project.Metadata.Choice import Option
from project.Metadata.Widget import Single_Choice, Vector_Editor
from project.Widget_Helpers import make_all_selector
Expand Down Expand Up @@ -155,6 +158,10 @@ type Dictionary key value
- key: The key to insert the value for.
- value: The value to associate with the `key`.

! Error Conditions
- If a floating-point value is used as a key, a
`Floating_Point_Equality` warning is attached.

> Example
Insert the value "seven" into the dictionary for the key 7.

Expand All @@ -163,9 +170,14 @@ type Dictionary key value
example_insert = Examples.dictionary.insert 7 "seven"
@key (make_all_selector ..Always)
@value (make_all_selector ..Always)
insert : Any -> Any -> Dictionary
insert self key=(Missing_Argument.throw "key") value=(Missing_Argument.throw "value") =
self.insert_builtin key value
insert : Any -> Any -> Boolean -> Dictionary
insert self key=(Missing_Argument.throw "key") value=(Missing_Argument.throw "value") no_warning:Boolean=False =
new_dict = self.insert_builtin key value
case key of
_ : Float ->
if no_warning then new_dict else
Warning.attach (Floating_Point_Equality.Used_As_Dictionary_Key key) new_dict
_ -> new_dict

## GROUP Selections
ICON table_clean
Expand Down
31 changes: 30 additions & 1 deletion distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import project.Data.Numbers.Float
import project.Data.Numbers.Integer
import project.Data.Text.Extensions
import project.Data.Text.Text
Expand Down Expand Up @@ -553,7 +554,7 @@ type Out_Of_Range
## PRIVATE
to_text : Text
to_text self =
extra = if self.message.is_nothing then "" else ": "+self.message.to_text
extra = if self.message.is_nothing then "" else " (message = "+self.message.to_text+")"
"(Out_Of_Range (value = "+self.value.to_text+")" + extra + ")"

## Indiciates that the response from a remote endpoint is over the size limit.
Expand All @@ -577,3 +578,31 @@ type Response_Too_Large
Convert the Java exception to an Enso dataflow error.
handle_java_exception ~action =
Panic.catch ResponseTooLargeException action (cause-> Error.throw (Response_Too_Large.Error cause.payload.getLimit))

## Indicates that some operation relies on equality on floating-point values,
which is not recommended.
type Floating_Point_Equality
## PRIVATE
Represents a general floating-point equality error, such as a direct comparison.
Error (location:Text)

## PRIVATE
Represents the use of a floating-point value as a `Dictionary` key.
Used_As_Dictionary_Key value:Float

## PRIVATE
Create a human-readable version of the error.
to_display_text : Text
to_display_text self = case self of
Floating_Point_Equality.Error location ->
"Relying on equality of floating-point numbers is not recommended (within "+location+")."
Floating_Point_Equality.Used_As_Dictionary_Key value ->
"Float used as dictionary key: "+value.to_text

## PRIVATE
to_text : Text
to_text self = case self of
Floating_Point_Equality.Error location ->
"(Error (location = "+location+"))"
Floating_Point_Equality.Used_As_Dictionary_Key value ->
"(Used_As_Dictionary_Key (value = "+value.to_text+"))"
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ distinct vector on =
key = on item
if (existing.get key False) then existing else
builder.append item
existing.insert key True
existing.insert key True no_warning=True

duplicates vector on = Vector.build builder->
counts = vector.fold Dictionary.empty current-> item->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from Standard.Base import all
import Standard.Base.Data.Read.Many_Files_List.Many_Files_List
import Standard.Base.Errors.Common.Index_Out_Of_Bounds
import Standard.Base.Errors.Common.Floating_Point_Equality
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Base.Internal.Rounding_Helpers
Expand All @@ -15,7 +16,7 @@ import Standard.Table.Internal.Widget_Helpers
import Standard.Table.Rows_To_Read.Rows_To_Read
from Standard.Table import Auto, Column, Data_Formatter, Previous_Value, Sort_Column, Table, Value_Type
from Standard.Table.Column import default_date_period
from Standard.Table.Errors import Conversion_Failure, Floating_Point_Equality, Inexact_Type_Coercion, Invalid_Value_Type
from Standard.Table.Errors import Conversion_Failure, Inexact_Type_Coercion, Invalid_Value_Type
from Standard.Table.Internal.Cast_Helpers import check_cast_compatibility

import project.Connection.Connection.Connection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Standard.Base.Data.Read.Many_Files_List.Many_Files_List
import Standard.Base.Data.Time.Errors.Date_Time_Format_Parse_Error
import Standard.Base.Data.Vector.Builder
import Standard.Base.Errors.Common.Additional_Warnings
import Standard.Base.Errors.Common.Floating_Point_Equality
import Standard.Base.Errors.Common.Incomparable_Values
import Standard.Base.Errors.Common.Index_Out_Of_Bounds
import Standard.Base.Errors.Common.Type_Error
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from Standard.Base import all
import Standard.Base.Errors.Common.No_Such_Method
import Standard.Base.Errors.Deprecated.Deprecated
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
from Standard.Base.Errors.Common import Floating_Point_Equality, No_Such_Method

import Standard.Table.Internal.Aggregate_Column_Helper
import Standard.Table.Internal.Aggregate_Column_Helper.Internal_Order_By_Column_Reference
import Standard.Table.Internal.Problem_Builder.Problem_Builder
from Standard.Table import Aggregate_Column
from Standard.Table.Aggregate_Column.Aggregate_Column import all
from Standard.Table.Errors import Floating_Point_Equality, No_Output_Columns
from Standard.Table.Errors import No_Output_Columns

import project.DB_Table.DB_Table
import project.Dialect.Dialect
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from Standard.Base import all

from Standard.Table.Errors import Floating_Point_Equality
import Standard.Base.Errors.Common.Floating_Point_Equality

import project.Internal.Helpers
import project.Internal.IR.SQL_Expression.SQL_Expression
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_State.Illegal_State
from Standard.Base.Errors.Common import Floating_Point_Equality

import Standard.Table.Internal.Join_Helpers
import Standard.Table.Internal.Unique_Name_Strategy.Unique_Name_Strategy
from Standard.Table import Join_Kind
from Standard.Table.Errors import Floating_Point_Equality

import project.Connection.Connection.Connection
import project.DB_Table.DB_Table
Expand Down
3 changes: 2 additions & 1 deletion distribution/lib/Standard/Table/0.0.0-dev/src/Column.enso
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Standard.Base.Data.Array_Proxy.Array_Proxy
import Standard.Base.Data.Read.Many_Files_List.Many_Files_List
import Standard.Base.Data.Vector.No_Wrap
import Standard.Base.Errors.Common.Arithmetic_Error
import Standard.Base.Errors.Common.Floating_Point_Equality
import Standard.Base.Errors.Common.Incomparable_Values
import Standard.Base.Errors.Common.Index_Out_Of_Bounds
import Standard.Base.Errors.Common.No_Such_Method
Expand Down Expand Up @@ -31,7 +32,7 @@ import project.Rows_To_Read.Rows_To_Read
import project.Table.Table
import project.Value_Type.Auto
import project.Value_Type.Value_Type
from project.Errors import Conversion_Failure, Floating_Point_Equality, Inexact_Type_Coercion, Invalid_Column_Names, Invalid_Value_Type, No_Index_Set_Error
from project.Errors import Conversion_Failure, Inexact_Type_Coercion, Invalid_Column_Names, Invalid_Value_Type, No_Index_Set_Error
from project.Internal.Column_Format import all
from project.Internal.Java_Exports import make_date_builder_adapter, make_string_builder
from project.Internal.Storage import enso_to_java, java_to_enso
Expand Down
13 changes: 0 additions & 13 deletions distribution/lib/Standard/Table/0.0.0-dev/src/Errors.enso
Original file line number Diff line number Diff line change
Expand Up @@ -169,19 +169,6 @@ type Ignored_Nothing_Values
to_display_text self =
"The column "+self.column+" contained Nothing values in rows " + self.rows.short_display_text + " which were ignored."

## Indicates that some operation relies on equality on floating-point values,
which is not recommended.
type Floating_Point_Equality
## PRIVATE
Error (location:Text)

## PRIVATE

Create a human-readable version of the error.
to_display_text : Text
to_display_text self =
"Relying on equality of floating-point numbers is not recommended (within "+self.location+")."

## Indicates that a text value with a delimiter was included in a concatenation
without any quote character
type Unquoted_Delimiter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ from Standard.Base import all
import Standard.Base.Data.Vector.No_Wrap
import Standard.Base.Errors.Common.Additional_Warnings
import Standard.Base.Errors.Common.Arithmetic_Error
import Standard.Base.Errors.Common.Floating_Point_Equality
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument

import project.Internal.Storage
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from Standard.Base import all
import Standard.Base.Data.Vector.Builder
import Standard.Base.Data.Vector.No_Wrap
import Standard.Base.Errors.Common.Floating_Point_Equality
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument

import project.Internal.Storage
import project.Internal.Value_Type_Helpers
import project.Value_Type.Value_Type
from project.Errors import Floating_Point_Equality, Missing_Input_Columns, No_Common_Type, No_Output_Columns, Unexpected_Extra_Columns
from project.Errors import Missing_Input_Columns, No_Common_Type, No_Output_Columns, Unexpected_Extra_Columns

polyglot java import org.enso.table.data.table.join.lookup.LookupColumnDescription

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from Standard.Base import all
import Standard.Base.Data.Array_Proxy.Array_Proxy
import Standard.Base.Errors.Common.Floating_Point_Equality
import Standard.Base.Errors.Illegal_State.Illegal_State

import project.Column.Column
import project.Value_Type.Value_Type
from project.Errors import Floating_Point_Equality

## PRIVATE
An Enso implementation mirroring `UnorderedMultiValueKey` from the Java
Expand Down
1 change: 1 addition & 0 deletions distribution/lib/Standard/Table/0.0.0-dev/src/Table.enso
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Standard.Base.Data.Read.Many_Files_List.Many_Files_List
import Standard.Base.Data.Time.Errors.Date_Time_Format_Parse_Error
import Standard.Base.Data.Vector.No_Wrap
import Standard.Base.Errors.Common.Additional_Warnings
import Standard.Base.Errors.Common.Floating_Point_Equality
import Standard.Base.Errors.Common.Incomparable_Values
import Standard.Base.Errors.Common.Index_Out_Of_Bounds
import Standard.Base.Errors.Common.No_Such_Method
Expand Down
11 changes: 11 additions & 0 deletions test/Base_Tests/src/Data/Dictionary_Spec.enso
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Errors.No_Such_Key.No_Such_Key
from Standard.Base.Errors.Common import Floating_Point_Equality

from Standard.Test import all

Expand Down Expand Up @@ -269,6 +270,16 @@ add_specs suite_builder =
m.at 200 . should_equal 3
m.at Nothing . should_equal 1

group_builder.specify "should attach a warning when a Float is used as a key" <|
m = Dictionary.empty . insert 1.2 3
m.at 1.2 . should_equal 3
Problems.expect_only_warning Floating_Point_Equality m

group_builder.specify "should attach a warning when a Float is used as a key (using from_vector)" <|
m = Dictionary.from_vector [[2, 3], [1.2, 3], [4, 5]]
m.at 1.2 . should_equal 3
Problems.expect_only_warning Floating_Point_Equality m

suite_builder.group "Polyglot keys and values" group_builder->
group_builder.specify "should support polyglot keys" pending=pending_js_missing <|
dict = Dictionary.singleton (js_str "A") 42
Expand Down
25 changes: 18 additions & 7 deletions test/Benchmarks/src/Map/Hash_Map.enso
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ polyglot java import org.enso.benchmark_helpers.JavaHashMapWrapper
options = Bench.options . set_warmup (Bench.phase_conf 2 2) . set_measure (Bench.phase_conf 2 3)

type Data
Value ~ints
Value ~dense_ints ~sparse_ints

create n =
create_ints =
create_dense_ints =
Vector.new n _->
Random.integer 0 ((n.div 100) - 1)
Data.Value create_ints
create_sparse_ints =
Vector.new n _->
Random.integer 0 (n - 1)
Data.Value create_dense_ints create_sparse_ints

type Scenario
Instance map_constructor
Expand Down Expand Up @@ -43,15 +46,23 @@ collect_benches = Bench.build builder->

builder.group ("Enso_Hash_Map_" + n.to_text) options group_builder->
# Scenario similar to what is done in distinct
# 'Sparse' ints have fewer duplicates and cause more inserts and fewer updates to happen.
group_builder.specify "Enso_Sparse_Incremental" <|
Scenario.Instance (_ -> Dictionary.empty) . run_distinct data.sparse_ints
group_builder.specify "Java_Sparse_Incremental" <|
Scenario.Instance (_ -> JavaHashMapWrapper.new) . run_distinct data.sparse_ints

# Scenario similar to what is done in distinct
# 'Dense' ints have more duplicates and cause fewer inserts and more updates to happen.
group_builder.specify "Enso_Incremental" <|
Scenario.Instance (_ -> Dictionary.empty) . run_distinct data.ints
Scenario.Instance (_ -> Dictionary.empty) . run_distinct data.dense_ints
group_builder.specify "Java_Incremental" <|
Scenario.Instance (_ -> JavaHashMapWrapper.new) . run_distinct data.ints
Scenario.Instance (_ -> JavaHashMapWrapper.new) . run_distinct data.dense_ints

# A scenario similar to what is done in add_row_number with grouping
group_builder.specify "Enso_Replacement" <|
Scenario.Instance (_ -> Dictionary.empty) . run_count_keys data.ints
Scenario.Instance (_ -> Dictionary.empty) . run_count_keys data.dense_ints
group_builder.specify "Java_Replacement" <|
Scenario.Instance (_ -> JavaHashMapWrapper.new) . run_count_keys data.ints
Scenario.Instance (_ -> JavaHashMapWrapper.new) . run_count_keys data.dense_ints

main = collect_benches . run_main
26 changes: 18 additions & 8 deletions test/Benchmarks/src/Vector/Distinct.enso
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,31 @@ options = Bench.options . set_warmup (Bench.phase_conf 1 3) . set_measure (Bench


type Data
Value ~random_vec ~uniform_vec ~random_text_vec ~uniform_text_vec
Value ~random_integer_vec ~uniform_integer_vec ~random_float_vec ~uniform_float_vec ~random_text_vec ~uniform_text_vec

create =
Data.Value create_random_vec create_uniform_vec create_random_text_vec create_uniform_text_vec
Data.Value create_random_integer_vec create_uniform_integer_vec create_random_float_vec create_uniform_float_vec create_random_text_vec create_uniform_text_vec


create_random_vec = Utils.make_random_vec 100000
create_random_integer_vec = Utils.make_random_integer_vec 100000


create_uniform_vec = Vector.fill 100000 1
create_random_float_vec = Utils.make_random_float_vec 100000


create_uniform_integer_vec = Vector.fill 100000 1


create_uniform_float_vec = Vector.fill 100000 1.2


create_random_text_vec =
random_vec = create_random_vec
random_vec = create_random_float_vec
random_vec.map .to_text


create_uniform_text_vec =
uniform_vec = create_uniform_vec
uniform_vec = create_uniform_integer_vec
uniform_vec.map .to_text


Expand All @@ -36,9 +42,13 @@ collect_benches = Bench.build builder->

builder.group "Vector_Distinct" options group_builder->
group_builder.specify "Random_Integer_Vector_Distinct_v2" <|
data.random_vec.distinct
data.random_integer_vec.distinct
group_builder.specify "Uniform_Integer_Vector_Distinct" <|
data.uniform_vec.distinct
data.uniform_integer_vec.distinct
group_builder.specify "Random_Float_Vector_Distinct_v2" <|
data.random_float_vec.distinct
group_builder.specify "Uniform_Float_Vector_Distinct" <|
data.uniform_float_vec.distinct
group_builder.specify "Random_Text_Vector_Distinct_v2" <|
data.random_text_vec.distinct
group_builder.specify "Uniform_Text_Vector_Distinct" <|
Expand Down
4 changes: 2 additions & 2 deletions test/Benchmarks/src/Vector/Operations.enso
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ options = Bench.options . set_warmup (Bench.phase_conf 5 5) . set_measure (Bench

collect_benches = Bench.build builder->
vector_size = 1000000
random_vec = Utils.make_random_vec vector_size
random_vec_2 = Utils.make_random_vec 100000
random_vec = Utils.make_random_float_vec vector_size
random_vec_2 = Utils.make_random_float_vec 100000
random_gen = Java_Random.new 123456
stateful_fun x =
s = State.get Number
Expand Down
Loading

0 comments on commit 159a7a3

Please sign in to comment.