kumi 0.0.25 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +70 -71
  4. data/data/functions/agg/boolean.yaml +6 -2
  5. data/data/functions/agg/numeric.yaml +32 -16
  6. data/data/functions/agg/string.yaml +4 -3
  7. data/data/functions/core/arithmetic.yaml +62 -14
  8. data/data/functions/core/boolean.yaml +12 -6
  9. data/data/functions/core/comparison.yaml +25 -13
  10. data/data/functions/core/constructor.yaml +16 -8
  11. data/data/functions/core/select.yaml +3 -1
  12. data/data/functions/core/stencil.yaml +14 -5
  13. data/data/functions/core/string.yaml +9 -4
  14. data/data/kernels/ruby/agg/numeric.yaml +1 -1
  15. data/docs/UNSAT_DETECTION.md +83 -0
  16. data/golden/array_element/expected/nast.txt +1 -1
  17. data/golden/array_element/expected/schema_ruby.rb +1 -1
  18. data/golden/array_index/expected/nast.txt +7 -7
  19. data/golden/array_index/expected/schema_ruby.rb +1 -1
  20. data/golden/array_operations/expected/nast.txt +2 -2
  21. data/golden/array_operations/expected/schema_ruby.rb +1 -1
  22. data/golden/array_operations/expected/snast.txt +3 -3
  23. data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
  24. data/golden/cascade_logic/expected/snast.txt +2 -2
  25. data/golden/chained_fusion/expected/nast.txt +2 -2
  26. data/golden/chained_fusion/expected/schema_ruby.rb +1 -1
  27. data/golden/element_arrays/expected/nast.txt +2 -2
  28. data/golden/element_arrays/expected/schema_ruby.rb +1 -1
  29. data/golden/element_arrays/expected/snast.txt +1 -1
  30. data/golden/empty_and_null_inputs/expected/nast.txt +3 -3
  31. data/golden/empty_and_null_inputs/expected/schema_ruby.rb +1 -1
  32. data/golden/function_overload/expected/ast.txt +29 -0
  33. data/golden/function_overload/expected/input_plan.txt +4 -0
  34. data/golden/function_overload/expected/lir_00_unoptimized.txt +18 -0
  35. data/golden/function_overload/expected/lir_01_hoist_scalar_references.txt +18 -0
  36. data/golden/function_overload/expected/lir_02_inlined.txt +20 -0
  37. data/golden/function_overload/expected/lir_03_cse.txt +20 -0
  38. data/golden/function_overload/expected/lir_04_1_loop_fusion.txt +20 -0
  39. data/golden/function_overload/expected/lir_04_loop_invcm.txt +20 -0
  40. data/golden/function_overload/expected/lir_06_const_prop.txt +20 -0
  41. data/golden/function_overload/expected/nast.txt +22 -0
  42. data/golden/function_overload/expected/schema_javascript.mjs +12 -0
  43. data/golden/function_overload/expected/schema_ruby.rb +39 -0
  44. data/golden/function_overload/expected/snast.txt +22 -0
  45. data/golden/function_overload/input.json +8 -0
  46. data/golden/function_overload/schema.kumi +19 -0
  47. data/golden/game_of_life/expected/lir_00_unoptimized.txt +4 -4
  48. data/golden/game_of_life/expected/lir_01_hoist_scalar_references.txt +4 -4
  49. data/golden/game_of_life/expected/lir_02_inlined.txt +16 -16
  50. data/golden/game_of_life/expected/lir_03_cse.txt +20 -16
  51. data/golden/game_of_life/expected/lir_04_1_loop_fusion.txt +20 -16
  52. data/golden/game_of_life/expected/lir_04_loop_invcm.txt +20 -16
  53. data/golden/game_of_life/expected/lir_06_const_prop.txt +20 -16
  54. data/golden/game_of_life/expected/nast.txt +4 -4
  55. data/golden/game_of_life/expected/schema_javascript.mjs +4 -2
  56. data/golden/game_of_life/expected/schema_ruby.rb +5 -3
  57. data/golden/game_of_life/expected/snast.txt +10 -10
  58. data/golden/hash_keys/expected/schema_ruby.rb +1 -1
  59. data/golden/hash_value/expected/nast.txt +1 -1
  60. data/golden/hash_value/expected/schema_ruby.rb +1 -1
  61. data/golden/hash_value/expected/snast.txt +1 -1
  62. data/golden/hierarchical_complex/expected/nast.txt +3 -3
  63. data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -1
  64. data/golden/hierarchical_complex/expected/snast.txt +3 -3
  65. data/golden/inline_rename_scope_leak/expected/nast.txt +3 -3
  66. data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +1 -1
  67. data/golden/input_reference/expected/nast.txt +2 -2
  68. data/golden/input_reference/expected/schema_ruby.rb +1 -1
  69. data/golden/interleaved_fusion/expected/nast.txt +2 -2
  70. data/golden/interleaved_fusion/expected/schema_ruby.rb +1 -1
  71. data/golden/let_inline/expected/nast.txt +4 -4
  72. data/golden/let_inline/expected/schema_ruby.rb +1 -1
  73. data/golden/loop_fusion/expected/nast.txt +1 -1
  74. data/golden/loop_fusion/expected/schema_ruby.rb +1 -1
  75. data/golden/min_reduce_scope/expected/nast.txt +3 -3
  76. data/golden/min_reduce_scope/expected/schema_ruby.rb +1 -1
  77. data/golden/min_reduce_scope/expected/snast.txt +1 -1
  78. data/golden/mixed_dimensions/expected/nast.txt +2 -2
  79. data/golden/mixed_dimensions/expected/schema_ruby.rb +1 -1
  80. data/golden/multirank_hoisting/expected/nast.txt +7 -7
  81. data/golden/multirank_hoisting/expected/schema_ruby.rb +1 -1
  82. data/golden/nested_hash/expected/nast.txt +1 -1
  83. data/golden/nested_hash/expected/schema_ruby.rb +1 -1
  84. data/golden/reduction_broadcast/expected/nast.txt +3 -3
  85. data/golden/reduction_broadcast/expected/schema_ruby.rb +1 -1
  86. data/golden/reduction_broadcast/expected/snast.txt +1 -1
  87. data/golden/roll/expected/schema_ruby.rb +1 -1
  88. data/golden/shift/expected/schema_ruby.rb +1 -1
  89. data/golden/shift_2d/expected/schema_ruby.rb +1 -1
  90. data/golden/simple_math/expected/lir_00_unoptimized.txt +1 -1
  91. data/golden/simple_math/expected/lir_01_hoist_scalar_references.txt +1 -1
  92. data/golden/simple_math/expected/lir_02_inlined.txt +1 -1
  93. data/golden/simple_math/expected/lir_03_cse.txt +1 -1
  94. data/golden/simple_math/expected/lir_04_1_loop_fusion.txt +1 -1
  95. data/golden/simple_math/expected/lir_04_loop_invcm.txt +1 -1
  96. data/golden/simple_math/expected/lir_06_const_prop.txt +1 -1
  97. data/golden/simple_math/expected/nast.txt +5 -5
  98. data/golden/simple_math/expected/schema_ruby.rb +1 -1
  99. data/golden/simple_math/expected/snast.txt +2 -2
  100. data/golden/streaming_basics/expected/nast.txt +8 -8
  101. data/golden/streaming_basics/expected/schema_ruby.rb +1 -1
  102. data/golden/streaming_basics/expected/snast.txt +1 -1
  103. data/golden/tuples/expected/lir_00_unoptimized.txt +5 -5
  104. data/golden/tuples/expected/lir_01_hoist_scalar_references.txt +5 -5
  105. data/golden/tuples/expected/lir_02_inlined.txt +5 -5
  106. data/golden/tuples/expected/lir_03_cse.txt +5 -5
  107. data/golden/tuples/expected/lir_04_1_loop_fusion.txt +5 -5
  108. data/golden/tuples/expected/lir_04_loop_invcm.txt +5 -5
  109. data/golden/tuples/expected/lir_06_const_prop.txt +5 -5
  110. data/golden/tuples/expected/nast.txt +4 -4
  111. data/golden/tuples/expected/schema_ruby.rb +1 -1
  112. data/golden/tuples/expected/snast.txt +6 -6
  113. data/golden/tuples_and_arrays/expected/lir_00_unoptimized.txt +1 -1
  114. data/golden/tuples_and_arrays/expected/lir_01_hoist_scalar_references.txt +1 -1
  115. data/golden/tuples_and_arrays/expected/lir_02_inlined.txt +2 -2
  116. data/golden/tuples_and_arrays/expected/lir_03_cse.txt +2 -2
  117. data/golden/tuples_and_arrays/expected/lir_04_1_loop_fusion.txt +2 -2
  118. data/golden/tuples_and_arrays/expected/lir_04_loop_invcm.txt +2 -2
  119. data/golden/tuples_and_arrays/expected/lir_06_const_prop.txt +2 -2
  120. data/golden/tuples_and_arrays/expected/nast.txt +3 -3
  121. data/golden/tuples_and_arrays/expected/schema_ruby.rb +1 -1
  122. data/golden/tuples_and_arrays/expected/snast.txt +2 -2
  123. data/golden/us_tax_2024/expected/ast.txt +63 -670
  124. data/golden/us_tax_2024/expected/input_plan.txt +8 -45
  125. data/golden/us_tax_2024/expected/lir_00_unoptimized.txt +253 -863
  126. data/golden/us_tax_2024/expected/lir_01_hoist_scalar_references.txt +253 -863
  127. data/golden/us_tax_2024/expected/lir_02_inlined.txt +1215 -5139
  128. data/golden/us_tax_2024/expected/lir_03_cse.txt +587 -2460
  129. data/golden/us_tax_2024/expected/lir_04_1_loop_fusion.txt +632 -2480
  130. data/golden/us_tax_2024/expected/lir_04_loop_invcm.txt +587 -2460
  131. data/golden/us_tax_2024/expected/lir_06_const_prop.txt +587 -2460
  132. data/golden/us_tax_2024/expected/nast.txt +123 -826
  133. data/golden/us_tax_2024/expected/schema_javascript.mjs +127 -581
  134. data/golden/us_tax_2024/expected/schema_ruby.rb +135 -610
  135. data/golden/us_tax_2024/expected/snast.txt +155 -858
  136. data/golden/us_tax_2024/expected.json +120 -1
  137. data/golden/us_tax_2024/input.json +18 -9
  138. data/golden/us_tax_2024/schema.kumi +48 -178
  139. data/golden/with_constants/expected/lir_00_unoptimized.txt +1 -1
  140. data/golden/with_constants/expected/lir_01_hoist_scalar_references.txt +1 -1
  141. data/golden/with_constants/expected/lir_02_inlined.txt +1 -1
  142. data/golden/with_constants/expected/lir_03_cse.txt +1 -1
  143. data/golden/with_constants/expected/lir_04_1_loop_fusion.txt +1 -1
  144. data/golden/with_constants/expected/lir_04_loop_invcm.txt +1 -1
  145. data/golden/with_constants/expected/lir_06_const_prop.txt +1 -1
  146. data/golden/with_constants/expected/nast.txt +2 -2
  147. data/golden/with_constants/expected/schema_ruby.rb +1 -1
  148. data/golden/with_constants/expected/snast.txt +2 -2
  149. data/lib/kumi/analyzer.rb +12 -12
  150. data/lib/kumi/core/analyzer/passes/formal_constraint_propagator.rb +236 -0
  151. data/lib/kumi/core/analyzer/passes/input_collector.rb +22 -4
  152. data/lib/kumi/core/analyzer/passes/nast_dimensional_analyzer_pass.rb +64 -18
  153. data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +9 -4
  154. data/lib/kumi/core/analyzer/passes/snast_pass.rb +3 -1
  155. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +172 -198
  156. data/lib/kumi/core/error_reporter.rb +36 -1
  157. data/lib/kumi/core/errors.rb +33 -1
  158. data/lib/kumi/core/functions/function_spec.rb +5 -4
  159. data/lib/kumi/core/functions/loader.rb +17 -1
  160. data/lib/kumi/core/functions/overload_resolver.rb +164 -0
  161. data/lib/kumi/core/functions/type_error_reporter.rb +118 -0
  162. data/lib/kumi/core/functions/type_rules.rb +155 -35
  163. data/lib/kumi/core/types/inference.rb +29 -22
  164. data/lib/kumi/core/types/normalizer.rb +29 -45
  165. data/lib/kumi/core/types/validator.rb +16 -27
  166. data/lib/kumi/core/types/value_objects.rb +116 -0
  167. data/lib/kumi/core/types.rb +45 -37
  168. data/lib/kumi/registry_v2/loader.rb +90 -0
  169. data/lib/kumi/registry_v2.rb +18 -1
  170. data/lib/kumi/version.rb +1 -1
  171. metadata +21 -7
  172. data/lib/kumi/core/analyzer/unsat_constant_evaluator.rb +0 -59
  173. data/lib/kumi/core/atom_unsat_solver.rb +0 -396
  174. data/lib/kumi/core/constraint_relationship_solver.rb +0 -641
  175. data/lib/kumi/core/types/builder.rb +0 -23
  176. data/lib/kumi/core/types/compatibility.rb +0 -96
  177. data/lib/kumi/core/types/formatter.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d0a680258ba7c40a2f00d9af8d4abf116344071ddc61d8b587a9a5a49eed6c9
4
- data.tar.gz: e16a0a52944cfa45d0a2f15c82fbbaac07392d4a1060f490b4c2fa67cb11208b
3
+ metadata.gz: b422f7930abf2d8300bcc0a6f565a4adf1505cb722f1d51abf42e776617eb255
4
+ data.tar.gz: b9920b8399e8521eb7517427a36118b0ccbdd9fb1d92644db822d9dbc755b0b6
5
5
  SHA512:
6
- metadata.gz: 6002d0e6a6b86c345226d630f09279b8e24c5ab581c1d6dba1c3f76b70f91e5ce0a9e2200ee402d12e0b43b593d326cf314ce19b5dafdd7249c36d467ce99a4e
7
- data.tar.gz: d58b16b88031e3c9ed75598f71e999657f3e6f23b8f0b9e2fe211c74f8f43d1aa43ff5125f6faef7c713cdb1fac5edd7e59e0aaf29405aafe7123708d352ee36
6
+ metadata.gz: d541c8a5839a7f8e8f5cd2c4e9e8e1418e474bca0d81152f4e0043a09e059ab4839bda084aa32c141938dfb3971815071219daa2e3d5733590ba8e2d84f830ba
7
+ data.tar.gz: 72f061aa65ec67d5f0d99812d5429225f5a623e0a5f49fdad27260fc6335629b85ae69dada088f05aef24b00a22c912194c8075c9d7373b43fbf433954f417ca
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.26] – 2025-10-17
4
+ ### Added
5
+ - UNSAT detection via constraint propagation through operations
6
+ - Documentation for UNSAT detection in `docs/UNSAT_DETECTION.md`
7
+
8
+ ### Changed
9
+ - Moved `UnsatDetector` to work over SNAST for full resolution context (axes/types)
10
+ - Refactored Typing system, added proper typing metadata on data/functions
11
+
3
12
  ## [0.0.25] – 2025-10-17
4
13
  ### Fixed
5
14
  - Fix inlining bug caused by previous fix
data/README.md CHANGED
@@ -16,94 +16,93 @@
16
16
 
17
17
  **Declarative calculation DSL for Ruby.** Write business rules once, run them anywhere.
18
18
 
19
- Kumi compiles high-level schemas into standalone Ruby and JavaScript with no runtime dependencies.
19
+ Kumi compiles high-level schemas into standalone Ruby and JavaScript with no runtime dependencies. Includes static analysis to catch impossible constraints before runtime.
20
20
 
21
21
  **Built for:** finance, tax, pricing, insurance, payroll, analytics—domains where correctness and transparency matter.
22
22
 
23
23
  ---
24
24
 
25
- ## Example: Conway's Game of Life
25
+ ## Example: US Tax Calculator (2024)
26
26
 
27
+ See the [interactive demo](https://kumi-play-web.fly.dev/) or browse the [golden test files](golden/us_tax_2024/) (schema, input, expected output, generated code).
27
28
 
28
29
  <details>
29
30
  <summary><strong>Schema</strong></summary>
30
31
 
31
32
  ```ruby
32
- module GameOfLife
33
- extend Kumi::Schema
34
-
35
- schema do
36
- input do
37
- array :rows do
38
- array :col do
39
- integer :alive # 0 or 1
33
+ schema do
34
+ input do
35
+ float :income
36
+ float :state_rate
37
+ float :local_rate
38
+ float :retirement_contrib
39
+ string :filing_status
40
+
41
+ array :statuses do
42
+ hash :status do
43
+ string :name
44
+ float :std
45
+ float :addl_threshold
46
+ array :rates do
47
+ hash :bracket do
48
+ float :lo
49
+ float :hi # -1 = open-ended
50
+ float :rate
51
+ end
40
52
  end
41
53
  end
42
54
  end
43
-
44
- let :a, input.rows.col.alive
45
-
46
- # axis_offset: 0 = x, 1 = y
47
- let :n, shift(a, -1, axis_offset: 1)
48
- let :s, shift(a, 1, axis_offset: 1)
49
- let :w, shift(a, -1)
50
- let :e, shift(a, 1)
51
- let :nw, shift(n, -1)
52
- let :ne, shift(n, 1)
53
- let :sw, shift(s, -1)
54
- let :se, shift(s, 1)
55
-
56
- let :neighbors, fn(:sum, [n, s, w, e, nw, ne, sw, se])
57
-
58
- # Conway rules
59
- let :alive, a > 0
60
- let :n3_alive, neighbors == 3
61
- let :n2_alive, neighbors == 2
62
- let :keep_alive, n2_alive & alive
63
-
64
- let :next_alive, n3_alive | keep_alive
65
-
66
- value :next_state, select(next_alive, 1, 0)
67
55
  end
68
56
 
57
+ # shared
58
+ let :big_hi, 100_000_000_000.0
59
+ let :state_tax, input.income * input.state_rate
60
+ let :local_tax, input.income * input.local_rate
61
+
62
+ # FICA constants
63
+ let :ss_wage_base, 168_600.0
64
+ let :ss_rate, 0.062
65
+ let :med_base_rate, 0.0145
66
+ let :addl_med_rate, 0.009
67
+
68
+ # per-status federal
69
+ let :taxable, fn(:max, [input.income - input.statuses.status.std, 0])
70
+ let :lo, input.statuses.status.rates.bracket.lo
71
+ let :hi, input.statuses.status.rates.bracket.hi
72
+ let :rate, input.statuses.status.rates.bracket.rate
73
+ let :hi_eff, select(hi == -1, big_hi, hi)
74
+ let :amt, fn(:clamp, taxable - lo, 0, hi_eff - lo)
75
+ let :fed_tax, fn(:sum, amt * rate)
76
+ let :in_br, (taxable >= lo) & (taxable < hi_eff)
77
+ let :fed_marg, fn(:sum_if, rate, in_br)
78
+ let :fed_eff, fed_tax / fn(:max, [input.income, 1.0])
79
+
80
+ # per-status FICA
81
+ let :ss_tax, fn(:min, [input.income, ss_wage_base]) * ss_rate
82
+ let :med_tax, input.income * med_base_rate
83
+ let :addl_med_tax, fn(:max, [input.income - input.statuses.status.addl_threshold, 0]) * addl_med_rate
84
+ let :fica_tax, ss_tax + med_tax + addl_med_tax
85
+ let :fica_eff, fica_tax / fn(:max, [input.income, 1.0])
86
+
87
+ # totals per status
88
+ let :total_tax, fed_tax + fica_tax + state_tax + local_tax
89
+ let :total_eff, total_tax / fn(:max, [input.income, 1.0])
90
+ let :after_tax, input.income - total_tax
91
+ let :take_home, after_tax - input.retirement_contrib
92
+
93
+ # array of result objects, one per status
94
+ value :summary, {
95
+ filing_status: input.statuses.status.name,
96
+ federal: { marginal: fed_marg, effective: fed_eff, tax: fed_tax },
97
+ fica: { effective: fica_eff, tax: fica_tax },
98
+ state: { marginal: input.state_rate, effective: input.state_rate, tax: state_tax },
99
+ local: { marginal: input.local_rate, effective: input.local_rate, tax: local_tax },
100
+ total: { effective: total_eff, tax: total_tax },
101
+ after_tax: after_tax,
102
+ retirement_contrib: input.retirement_contrib,
103
+ take_home: take_home
104
+ }
69
105
  end
70
- ````
71
-
72
- </details>
73
-
74
-
75
- <details>
76
- <summary><strong>Generated JavaScript (excerpt)</strong></summary>
77
-
78
- ```js
79
- export function _next_state(input) {
80
- let out = [];
81
- let t285 = input["rows"];
82
- let t1539 = t285.length;
83
- const t1540 = -1;
84
- const t1542 = 0;
85
- const t1546 = 1;
86
- const t1334 = 3;
87
- const t1339 = 2;
88
- let t1547 = t1539 - t1546;
89
- t285.forEach((rows_el_286, rows_i_287) => {
90
- let out_1 = [];
91
- let t1541 = rows_i_287 - t1540;
92
- let t1561 = rows_i_287 - t1546;
93
- let t1580 = ((rows_i_287 % t1539) + t1539) % t1539;
94
- // ... neighbor calculations, Conway's rules
95
- let t1332 = [t1557, t1577, t1597, t1617, t1645, t1673, t1701, t1729];
96
- let t1333 = t1332.reduce((a, b) => a + b, 0);
97
- let t1335 = t1333 == t1334;
98
- let t1340 = t1333 == t1339;
99
- let t1344 = col_el_288 > t1542;
100
- let t1345 = t1340 && t1344;
101
- let t528 = t1335 || t1345;
102
- let t293 = t528 ? t1546 : t1542;
103
- out_1.push(t293);
104
- });
105
- return out;
106
- }
107
106
  ```
108
107
 
109
108
  </details>
@@ -2,13 +2,17 @@ functions:
2
2
  - id: agg.any
3
3
  kind: reduce
4
4
  params: [{ name: source_value }]
5
- dtype: "boolean"
5
+ dtype:
6
+ rule: scalar
7
+ kind: boolean
6
8
  aliases: ["any", "any?"]
7
9
  folding_class_method: any?
8
10
 
9
11
  - id: agg.all
10
12
  kind: reduce
11
13
  params: [{ name: source_value }]
12
- dtype: "boolean"
14
+ dtype:
15
+ rule: scalar
16
+ kind: boolean
13
17
  aliases: ["all", "all?"]
14
18
  folding_class_method: all?
@@ -2,65 +2,79 @@ functions:
2
2
  - id: agg.sum
3
3
  kind: reduce
4
4
  params: [{ name: source_value }]
5
- dtype: "same_as(source_value)"
5
+ dtype:
6
+ rule: same_as
7
+ param: source_value
6
8
  reduction_strategy: identity # This function has a true identity value.
7
9
  aliases: ["sum"]
8
-
10
+
9
11
  - id: agg.count
10
12
  kind: reduce
11
13
  params: [{ name: source_value }]
12
- dtype: "integer"
14
+ dtype:
15
+ rule: scalar
16
+ kind: integer
13
17
  reduction_strategy: identity # This function has a true identity value.
14
18
  aliases: ["count"]
15
19
 
16
20
  - id: agg.min
17
21
  kind: reduce
18
22
  params: [{ name: source_value }]
19
- dtype: "element_of(source_value)"
23
+ dtype:
24
+ rule: element_of
25
+ param: source_value
20
26
  reduction_strategy: first_element
21
27
  aliases: ["min"]
22
28
 
23
29
  - id: agg.max
24
30
  kind: reduce
25
31
  params: [{ name: source_value }]
26
- dtype: "element_of(source_value)"
32
+ dtype:
33
+ rule: element_of
34
+ param: source_value
27
35
  reduction_strategy: first_element
28
36
  aliases: ["max"]
29
-
37
+
30
38
  - id: agg.mean
31
39
  kind: reduce
32
40
  params: [{ name: source_value }]
33
- dtype: "float"
41
+ dtype:
42
+ rule: scalar
43
+ kind: float
34
44
  aliases: ["mean", "avg"]
35
45
  expand:
36
46
  fn: div
37
47
  args:
38
48
  - fn: sum
39
- args: [ "$1" ] # $1 is source_value
49
+ args: ["$1"] # $1 is source_value
40
50
  - fn: count
41
- args: [ "$1" ]
51
+ args: ["$1"]
42
52
 
43
53
  - id: agg.sum_if
44
54
  kind: reduce
45
55
  params: [{ name: source_value }, { name: condition }]
46
- dtype: "same_as(source_value)"
56
+ dtype:
57
+ rule: same_as
58
+ param: source_value
47
59
  aliases: ["sum_if"]
48
60
  expand:
49
61
  fn: sum
50
62
  args:
51
63
  - fn: __select__
52
- args: [ "$2", "$1", { const: 0 } ] # $1=source_value, $2=condition
64
+ args: ["$2", "$1", { const: 0 }] # $1=source_value, $2=condition
53
65
 
54
66
  - id: agg.count_if
55
67
  kind: reduce
56
68
  params: [{ name: condition }, { name: source_value }]
57
- dtype: "integer"
69
+ dtype:
70
+ rule: scalar
71
+ kind: integer
58
72
  aliases: ["count_if"]
59
73
  expand:
60
74
  fn: sum # Summing 1s and 0s is equivalent to a conditional count
61
75
  args:
62
76
  - fn: __select__
63
- args:
77
+ args:
64
78
  - fn: eq
65
79
  args: ["$1", "$2"]
66
80
  - { const: 1 }
@@ -69,12 +83,14 @@ functions:
69
83
  - id: agg.mean_if
70
84
  kind: reduce
71
85
  params: [{ name: source_value }, { name: condition }]
72
- dtype: "float"
86
+ dtype:
87
+ rule: scalar
88
+ kind: float
73
89
  aliases: ["avg_if", "mean_if"]
74
90
  expand:
75
91
  fn: div
76
92
  args:
77
93
  - fn: sum_if
78
- args: [ "$1", "$2" ] # Sum the values where the condition is true
94
+ args: ["$1", "$2"] # Sum the values where the condition is true
79
95
  - fn: count_if
80
- args: [ "$1", "$2" ] # Count the values where the condition is true
96
+ args: ["$1", "$2"] # Count the values where the condition is true
@@ -2,7 +2,8 @@ functions:
2
2
  - id: agg.join
3
3
  kind: reduce
4
4
  params: [{ name: source_value, dtype: string }]
5
- dtype: "string"
5
+ dtype:
6
+ rule: scalar
7
+ kind: string
6
8
  reduction_strategy: first_element
7
- aliases: ["join"]
8
-
9
+ aliases: ["join"]
@@ -2,49 +2,97 @@ functions:
2
2
  - id: core.abs
3
3
  kind: elementwise
4
4
  params: [{ name: number }]
5
- dtype: "same_as(number)"
5
+ dtype:
6
+ rule: same_as
7
+ param: number
6
8
  aliases: ["abs"]
7
9
 
8
10
  - id: core.add
9
- kind: elementwise
11
+ kind: elementwise
10
12
  params: [{ name: left_operand }, { name: right_operand }]
11
- dtype: "promote(left_operand,right_operand)"
13
+ dtype:
14
+ rule: promote
15
+ params: [left_operand, right_operand]
12
16
  aliases: ["add"]
17
+ constraint_semantics:
18
+ domain_effect: EXTEND
19
+ pure_combiner: true
20
+ commutativity: true
21
+ associativity: true
22
+ identity: 0
23
+ forward_propagation:
24
+ # If we know left == L and right == R, then result == L + R
25
+ equality: "result = left + right"
26
+ # If result is in [a, b] and left/right in domains, derive tighter bounds
27
+ range: "result_min = left_min + right_min; result_max = left_max + right_max"
28
+ reverse_propagation:
29
+ # If result == V, then left == V - right
30
+ equality: "left = result - right; right = result - left"
31
+ # If result in [a, b] and left in [l1, l2], then right in [a - l2, b - l1]
32
+ range: "left_min = result_min - right_max; left_max = result_max - right_min; right_min = result_min - left_max; right_max = result_max - left_min"
13
33
 
14
34
  - id: core.sub
15
35
  kind: elementwise
16
- params: [{ name: left_operand }, { name: right_operand }]
17
- dtype: "promote(left_operand,right_operand)"
36
+ params: [{ name: left_operand }, { name: right_operand }]
37
+ dtype:
38
+ rule: promote
39
+ params: [left_operand, right_operand]
18
40
  aliases: ["sub", "subtract"]
19
41
 
20
42
  - id: core.mul
21
43
  kind: elementwise
22
44
  params: [{ name: left_operand }, { name: right_operand }]
23
- dtype: "promote(left_operand,right_operand)"
45
+ dtype:
46
+ rule: promote
47
+ params: [left_operand, right_operand]
24
48
  aliases: ["mul", "multiply"]
49
+ constraint_semantics:
50
+ domain_effect: SCALE
51
+ pure_combiner: true
52
+ commutativity: true
53
+ associativity: true
54
+ identity: 1
55
+ forward_propagation:
56
+ # If we know left == L and right == R, then result == L * R
57
+ equality: "result = left * right"
58
+ # For multiplication: result bounds depend on sign of operands
59
+ range: "result = combinations of {left_min*right_min, left_min*right_max, left_max*right_min, left_max*right_max} -> [min, max]"
60
+ reverse_propagation:
61
+ # If result == V and right != 0, then left == V / right
62
+ equality: "left = result / right (if right != 0); right = result / left (if left != 0)"
63
+ # For multiplication: divide by absolute values considering sign changes
64
+ range: "derive from result bounds and operand bounds considering sign combinations"
25
65
 
26
66
  - id: core.pow
27
67
  kind: elementwise
28
68
  params: [{ name: base }, { name: exponent }]
29
- dtype: "promote(base,exponent)"
69
+ dtype:
70
+ rule: promote
71
+ params: [base, exponent]
30
72
  aliases: ["pow", "power"]
31
73
 
32
74
  - id: core.div
33
- kind: elementwise
75
+ kind: elementwise
34
76
  params: [{ name: left_operand }, { name: right_operand }]
35
- dtype: "float"
77
+ dtype:
78
+ rule: scalar
79
+ kind: float
36
80
  aliases: ["div", "divide"]
37
81
 
38
82
  - id: core.mod
39
83
  kind: elementwise
40
84
  params:
41
- - { name: left_operand}
42
- - { name: right_operand}
43
- dtype: "promote(left_operand,right_operand)"
85
+ - { name: left_operand }
86
+ - { name: right_operand }
87
+ dtype:
88
+ rule: promote
89
+ params: [left_operand, right_operand]
44
90
  aliases: ["mod", "modulo", "%"]
45
91
 
46
92
  - id: core.clamp
47
93
  kind: elementwise
48
- params: [{name: x},{name: lo},{name: hi}]
49
- dtype: "same_as(x)"
94
+ params: [{ name: x }, { name: lo }, { name: hi }]
95
+ dtype:
96
+ rule: same_as
97
+ param: x
50
98
  aliases: ["clamp"]
@@ -2,17 +2,23 @@ functions:
2
2
  - id: core.and
3
3
  kind: elementwise
4
4
  params: [{ name: left_operand }, { name: right_operand }]
5
- dtype: "boolean"
6
- aliases : ["and", "&"]
5
+ dtype:
6
+ rule: scalar
7
+ kind: boolean
8
+ aliases: ["and", "&"]
7
9
 
8
10
  - id: core.or
9
11
  kind: elementwise
10
12
  params: [{ name: left_operand }, { name: right_operand }]
11
- dtype: "boolean"
12
- aliases : ["or", "|"]
13
+ dtype:
14
+ rule: scalar
15
+ kind: boolean
16
+ aliases: ["or", "|"]
13
17
 
14
18
  - id: core.not
15
19
  kind: elementwise
16
20
  params: [{ name: operand }]
17
- dtype: "boolean"
18
- aliases : ["not", "!"]
21
+ dtype:
22
+ rule: scalar
23
+ kind: boolean
24
+ aliases: ["not", "!"]
@@ -2,41 +2,53 @@ functions:
2
2
  - id: core.gte
3
3
  kind: elementwise
4
4
  params: [{ name: left_operand }, { name: right_operand }]
5
- dtype: "boolean"
6
- aliases : ["gte", "ge", "greater_than_or_equal", ">="]
5
+ dtype:
6
+ rule: scalar
7
+ kind: boolean
8
+ aliases: ["gte", "ge", "greater_than_or_equal", ">="]
7
9
  folding_class_method: gte
8
10
 
9
11
  - id: core.gt
10
12
  kind: elementwise
11
13
  params: [{ name: left_operand }, { name: right_operand }]
12
- dtype: "boolean"
13
- aliases : ["gt", "greater_than", ">"]
14
+ dtype:
15
+ rule: scalar
16
+ kind: boolean
17
+ aliases: ["gt", "greater_than", ">"]
14
18
  folding_class_method: gt
15
19
 
16
20
  - id: core.lte
17
21
  kind: elementwise
18
22
  params: [{ name: left_operand }, { name: right_operand }]
19
- dtype: "boolean"
20
- aliases : ["lte", "le", "less_than_or_equal", "<="]
23
+ dtype:
24
+ rule: scalar
25
+ kind: boolean
26
+ aliases: ["lte", "le", "less_than_or_equal", "<="]
21
27
  folding_class_method: lte
22
28
 
23
29
  - id: core.lt
24
30
  kind: elementwise
25
31
  params: [{ name: left_operand }, { name: right_operand }]
26
- dtype: "boolean"
27
- aliases : ["lt", "less_than", "<"]
32
+ dtype:
33
+ rule: scalar
34
+ kind: boolean
35
+ aliases: ["lt", "less_than", "<"]
28
36
  folding_class_method: lt
29
37
 
30
38
  - id: core.eq
31
- kind: elementwise
39
+ kind: elementwise
32
40
  params: [{ name: left_operand }, { name: right_operand }]
33
- dtype: "boolean"
34
- aliases : ["eq", "equal", "=="]
41
+ dtype:
42
+ rule: scalar
43
+ kind: boolean
44
+ aliases: ["eq", "equal", "=="]
35
45
  folding_class_method: eq
36
46
 
37
47
  - id: core.neq
38
48
  kind: elementwise
39
49
  params: [{ name: left_operand }, { name: right_operand }]
40
- dtype: "boolean"
41
- aliases : ["neq", "not_equal", "!="]
50
+ dtype:
51
+ rule: scalar
52
+ kind: boolean
53
+ aliases: ["neq", "not_equal", "!="]
42
54
  folding_class_method: neq
@@ -1,32 +1,40 @@
1
1
  functions:
2
- # length is probably in the wrong place
2
+ # length is probably in the wrong place
3
3
  - id: core.length
4
4
  kind: elementwise
5
5
  params: [{ name: collection, dtype: string }]
6
- dtype: "integer"
6
+ dtype:
7
+ rule: scalar
8
+ kind: integer
7
9
  aliases: ["length", "len", "size"]
8
10
  folding_class_method: length
9
11
 
10
12
  - id: core.array_size
11
13
  kind: elementwise
12
14
  params: [{ name: collection, dtype: array }]
13
- dtype: "integer"
14
- aliases: ["array_size","size"]
15
+ dtype:
16
+ rule: scalar
17
+ kind: integer
18
+ aliases: ["array_size", "size"]
15
19
  folding_class_method: length
16
20
 
17
21
  - id: core.at
18
22
  kind: elementwise
19
- params:
23
+ params:
20
24
  - { name: collection }
21
25
  - { name: index, dtype: integer }
22
- dtype: "element_of(collection)"
26
+ dtype:
27
+ rule: element_of
28
+ param: collection
23
29
  aliases: ["at", "get", "[]"]
24
30
  folding_class_method: at
25
31
 
26
32
  - id: core.hash_fetch
27
33
  kind: elementwise
28
- params:
34
+ params:
29
35
  - { name: key, dtype: hash }
30
- dtype: "value_of(key)" # TODO, we can calculate,
36
+ dtype:
37
+ rule: scalar
38
+ kind: any
31
39
  aliases: ["fetch"]
32
40
  folding_class_method: "[]"
@@ -2,6 +2,8 @@ functions:
2
2
  - id: __select__
3
3
  kind: elementwise
4
4
  params: [{ name: condition_mask }, { name: value_when_true }, { name: value_when_false }]
5
- dtype: "same_as(value_when_true)"
5
+ dtype:
6
+ rule: same_as
7
+ param: value_when_true
6
8
  aliases: ["select", "if"]
7
9
  folding_class_method: select
@@ -1,8 +1,10 @@
1
1
  functions:
2
2
  - id: roll
3
3
  kind: elementwise
4
- params: [{ name: source }, { name: offset }]
5
- dtype: "same_as(source)"
4
+ params: [{ name: source }, { name: offset }]
5
+ dtype:
6
+ rule: same_as
7
+ param: source
6
8
  options:
7
9
  policy: wrap # wrap|clamp
8
10
  axis_offset: 0
@@ -10,12 +12,19 @@ functions:
10
12
  - id: shift
11
13
  kind: elementwise
12
14
  params: [{ name: source }, { name: offset }]
13
- dtype: "same_as(source)"
15
+ dtype:
16
+ rule: same_as
17
+ param: source
14
18
  options:
15
19
  policy: zero # zero|clamp
16
20
  axis_offset: 0
21
+ constraint_semantics:
22
+ domain_effect: NONE
23
+ pure_combiner: false
17
24
 
18
25
  - id: index
19
26
  kind: elementwise
20
- params: [{name: index_name, dtype: string }]
21
- dtype: integer
27
+ params: [{ name: index_name, dtype: string }]
28
+ dtype:
29
+ rule: scalar
30
+ kind: integer
@@ -2,18 +2,23 @@ functions:
2
2
  - id: core.concat
3
3
  kind: elementwise
4
4
  params: [{ name: left_string }, { name: right_string }]
5
- dtype: "string"
5
+ dtype:
6
+ rule: scalar
7
+ kind: string
6
8
  aliases: ["concat"]
7
9
 
8
10
  - id: core.upcase
9
11
  kind: elementwise
10
12
  params: [{ name: string }]
11
- dtype: "string"
13
+ dtype:
14
+ rule: scalar
15
+ kind: string
12
16
  aliases: ["upcase"]
13
17
 
14
18
  - id: core.downcase
15
19
  kind: elementwise
16
20
  params: [{ name: string }]
17
- dtype: "string"
21
+ dtype:
22
+ rule: scalar
23
+ kind: string
18
24
  aliases: ["downcase"]
19
-
@@ -4,7 +4,7 @@ kernels:
4
4
  inline: "+= $1"
5
5
  impl: "(a,b)\n a + b"
6
6
  fold_inline: "= $0.sum"
7
- identity:
7
+ identity:
8
8
  float: 0.0
9
9
  integer: 0
10
10