relationalai 0.11.2__py3-none-any.whl → 0.11.4__py3-none-any.whl

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 (42) hide show
  1. relationalai/clients/snowflake.py +44 -15
  2. relationalai/clients/types.py +1 -0
  3. relationalai/clients/use_index_poller.py +446 -178
  4. relationalai/early_access/builder/std/__init__.py +1 -1
  5. relationalai/early_access/dsl/bindings/csv.py +4 -4
  6. relationalai/semantics/internal/internal.py +22 -4
  7. relationalai/semantics/lqp/executor.py +69 -18
  8. relationalai/semantics/lqp/intrinsics.py +23 -0
  9. relationalai/semantics/lqp/model2lqp.py +16 -6
  10. relationalai/semantics/lqp/passes.py +3 -4
  11. relationalai/semantics/lqp/primitives.py +38 -14
  12. relationalai/semantics/metamodel/builtins.py +152 -11
  13. relationalai/semantics/metamodel/factory.py +3 -2
  14. relationalai/semantics/metamodel/helpers.py +78 -2
  15. relationalai/semantics/reasoners/graph/core.py +343 -40
  16. relationalai/semantics/reasoners/optimization/solvers_dev.py +20 -1
  17. relationalai/semantics/reasoners/optimization/solvers_pb.py +24 -3
  18. relationalai/semantics/rel/compiler.py +5 -17
  19. relationalai/semantics/rel/executor.py +2 -2
  20. relationalai/semantics/rel/rel.py +6 -0
  21. relationalai/semantics/rel/rel_utils.py +37 -1
  22. relationalai/semantics/rel/rewrite/extract_common.py +153 -242
  23. relationalai/semantics/sql/compiler.py +540 -202
  24. relationalai/semantics/sql/executor/duck_db.py +21 -0
  25. relationalai/semantics/sql/executor/result_helpers.py +7 -0
  26. relationalai/semantics/sql/executor/snowflake.py +9 -2
  27. relationalai/semantics/sql/rewrite/denormalize.py +4 -6
  28. relationalai/semantics/sql/rewrite/recursive_union.py +23 -3
  29. relationalai/semantics/sql/sql.py +120 -46
  30. relationalai/semantics/std/__init__.py +9 -4
  31. relationalai/semantics/std/datetime.py +363 -0
  32. relationalai/semantics/std/math.py +77 -0
  33. relationalai/semantics/std/re.py +83 -0
  34. relationalai/semantics/std/strings.py +1 -1
  35. relationalai/tools/cli_controls.py +445 -60
  36. relationalai/util/format.py +78 -1
  37. {relationalai-0.11.2.dist-info → relationalai-0.11.4.dist-info}/METADATA +3 -2
  38. {relationalai-0.11.2.dist-info → relationalai-0.11.4.dist-info}/RECORD +41 -39
  39. relationalai/semantics/std/dates.py +0 -213
  40. {relationalai-0.11.2.dist-info → relationalai-0.11.4.dist-info}/WHEEL +0 -0
  41. {relationalai-0.11.2.dist-info → relationalai-0.11.4.dist-info}/entry_points.txt +0 -0
  42. {relationalai-0.11.2.dist-info → relationalai-0.11.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,363 @@
1
+ from __future__ import annotations
2
+ from typing import Union, Literal
3
+ import datetime as dt
4
+
5
+ from relationalai.semantics.internal import internal as b
6
+ from .std import _DateTime, _Date, _Integer, _String, _make_expr
7
+ from .. import std
8
+
9
+ class ISO:
10
+ DATE = "yyyy-mm-dd"
11
+ HOURS = "yyyy-mm-ddTHH"
12
+ HOURS_TZ = "yyyy-mm-ddTHHz"
13
+ MINUTES = "yyyy-mm-ddTHH:MM"
14
+ MINUTES_TZ = "yyyy-mm-ddTHH:MMz"
15
+ SECONDS = "yyyy-mm-ddTHH:MM:SS"
16
+ SECONDS_TZ = "yyyy-mm-ddTHH:MM:SSz"
17
+ MILLIS = "yyyy-mm-ddTHH:MM:SS.s"
18
+ MILLIS_TZ = "yyyy-mm-ddTHH:MM:SS.sz"
19
+
20
+ #--------------------------------------------------
21
+ # Date functions
22
+ #--------------------------------------------------
23
+
24
+ class date:
25
+
26
+ def __new__(cls, year: _Integer, month: _Integer, day: _Integer) -> b.Expression:
27
+ return _make_expr("construct_date", std.cast_to_int64(year), std.cast_to_int64(month), std.cast_to_int64(day), b.Date.ref("res"))
28
+
29
+ @classmethod
30
+ def year(cls, date: _Date) -> b.Expression:
31
+ return _make_expr("date_year", date, b.Int64.ref("res"))
32
+
33
+ @classmethod
34
+ def quarter(cls, date: _Date) -> b.Expression:
35
+ return _make_expr("date_quarter", date, b.Int64.ref("res"))
36
+
37
+ @classmethod
38
+ def month(cls, date: _Date) -> b.Expression:
39
+ return _make_expr("date_month", date, b.Int64.ref("res"))
40
+
41
+ @classmethod
42
+ def week(cls, date: _Date) -> b.Expression:
43
+ return _make_expr("date_week", date, b.Int64.ref("res"))
44
+
45
+ @classmethod
46
+ def day(cls, date: _Date) -> b.Expression:
47
+ return _make_expr("date_day", date, b.Int64.ref("res"))
48
+
49
+ @classmethod
50
+ def dayofyear(cls, date: _Date) -> b.Expression:
51
+ return _make_expr("date_dayofyear", date, b.Int64.ref("res"))
52
+
53
+ @classmethod
54
+ def isoweekday(cls, date: _Date) -> b.Expression:
55
+ """
56
+ Return the ISO weekday as an integer, where Monday is 1, and Sunday is 7.
57
+ """
58
+ return _make_expr("date_weekday", date, b.Int64.ref("res"))
59
+
60
+ @classmethod
61
+ def weekday(cls, date: _Date) -> b.Expression:
62
+ return cls.isoweekday(date) - 1 # Convert ISO weekday (1=Mon..7=Sun) to weekday (0=Mon..6=Sun)
63
+
64
+ @classmethod
65
+ def fromordinal(cls, ordinal: _Integer) -> b.Expression:
66
+ # ordinal 1 = '0001-01-01'. Minus 1 day since we can't declare date 0000-00-00
67
+ return cls.add(b.Date(dt.date(1, 1, 1)), days(ordinal - 1))
68
+
69
+ @classmethod
70
+ def to_datetime(cls, date: _Date, hour: int = 0, minute: int = 0, second: int = 0, millisecond: int = 0, tz: str = "UTC") -> b.Expression:
71
+ _year = cls.year(date)
72
+ _month = cls.month(date)
73
+ _day = cls.day(date)
74
+ return _make_expr("construct_datetime_ms_tz", _year, _month, _day, hour, minute, second, millisecond, tz, b.DateTime.ref("res"))
75
+
76
+ @classmethod
77
+ def format(cls, date: _Date, format: _String) -> b.Expression:
78
+ return _make_expr("date_format", date, format, b.String.ref("res"))
79
+
80
+ @classmethod
81
+ def add(cls, date: _Date, period: b.Producer) -> b.Expression:
82
+ return _make_expr("date_add", date, period, b.Date.ref("res"))
83
+
84
+ @classmethod
85
+ def subtract(cls, date: _Date, period: b.Producer) -> b.Expression:
86
+ return _make_expr("date_subtract", date, period, b.Date.ref("res"))
87
+
88
+ @classmethod
89
+ def range(cls, start: _Date | None = None, end: _Date | None = None, periods: int = 1, freq: Frequency = "D") -> b.Expression:
90
+ """
91
+ Note on date_ranges and datetime_range: The way the computation works is that it first overapproximates the
92
+ number of periods.
93
+
94
+ For example, date_range(2025-02-01, 2025-03-01, freq='M') and date_range(2025-02-01, 2025-03-31, freq='M') will
95
+ compute range_end to be ceil(28*1/(365/12))=1 and ceil(58*1/(365/12))=2.
96
+
97
+ Then, the computation fetches range_end+1 items into _date, which is the right number in the first case but
98
+ one too many in the second case. That's why a filter end >= _date (or variant of) is applied, to remove any
99
+ extra item. The result is two items in both cases.
100
+ """
101
+ if start is None and end is None:
102
+ raise ValueError("Invalid start/end date for date_range. Must provide at least start date or end date")
103
+ _days = {
104
+ "D": 1,
105
+ "W": 1/7,
106
+ "M": 1/(365/12),
107
+ "Y": 1/365,
108
+ }
109
+ if freq not in _days.keys():
110
+ raise ValueError(f"Frequency '{freq}' is not allowed for date_range. List of allowed frequencies: {list(_days.keys())}")
111
+ date_func = cls.add
112
+ if start is None:
113
+ start = end
114
+ end = None
115
+ date_func = cls.subtract
116
+ assert start is not None
117
+ if end is not None:
118
+ num_days = cls.period_days(start, end)
119
+ if freq in ["W", "M", "Y"]:
120
+ range_end = std.cast(b.Int64, std.math.ceil(num_days * _days[freq]))
121
+ else:
122
+ range_end = num_days
123
+ # date_range is inclusive. add 1 since std.range is exclusive
124
+ ix = std.range(0, range_end + 1, 1)
125
+ else:
126
+ ix = std.range(0, periods, 1)
127
+ _date = date_func(start, _periods[freq](ix))
128
+ if isinstance(end, dt.date) :
129
+ return b.Date(end) >= _date
130
+ elif end is not None:
131
+ return end >= _date
132
+ return _date
133
+
134
+ @classmethod
135
+ def period_days(cls, start: _Date, end: _Date) -> b.Expression:
136
+ return _make_expr("dates_period_days", start, end, b.Int64.ref("res"))
137
+
138
+ @classmethod
139
+ def fromisoformat(cls, date_string: _String) -> b.Expression:
140
+ return _make_expr("parse_date", date_string, ISO.DATE, b.Date.ref("res"))
141
+
142
+ #--------------------------------------------------
143
+ # DateTime functions
144
+ #--------------------------------------------------
145
+
146
+ class datetime:
147
+
148
+ def __new__(cls, year: _Integer, month: _Integer, day: _Integer, hour: _Integer = 0, minute: _Integer = 0,
149
+ second: _Integer = 0, millisecond: _Integer = 0, tz: dt.tzinfo|_String = "UTC") -> b.Expression:
150
+ if isinstance(tz, dt.tzinfo):
151
+ tz = str(tz)
152
+ return _make_expr("construct_datetime_ms_tz", std.cast_to_int64(year), std.cast_to_int64(month),
153
+ std.cast_to_int64(day), std.cast_to_int64(hour), std.cast_to_int64(minute),
154
+ std.cast_to_int64(second), std.cast_to_int64(millisecond), tz, b.DateTime.ref("res"))
155
+
156
+ @classmethod
157
+ def now(cls) -> b.Expression:
158
+ return _make_expr("datetime_now", b.DateTime.ref("res"))
159
+
160
+ @classmethod
161
+ def year(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
162
+ tz = _extract_tz(datetime, tz)
163
+ return _make_expr("datetime_year", datetime, tz, b.Int64.ref("res"))
164
+
165
+ @classmethod
166
+ def quarter(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
167
+ tz = _extract_tz(datetime, tz)
168
+ return _make_expr("datetime_quarter", datetime, tz, b.Int64.ref("res"))
169
+
170
+ @classmethod
171
+ def month(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
172
+ tz = _extract_tz(datetime, tz)
173
+ return _make_expr("datetime_month", datetime, tz, b.Int64.ref("res"))
174
+
175
+ @classmethod
176
+ def week(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
177
+ tz = _extract_tz(datetime, tz)
178
+ return _make_expr("datetime_week", datetime, tz, b.Int64.ref("res"))
179
+
180
+ @classmethod
181
+ def day(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
182
+ tz = _extract_tz(datetime, tz)
183
+ return _make_expr("datetime_day", datetime, tz, b.Int64.ref("res"))
184
+
185
+ @classmethod
186
+ def dayofyear(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
187
+ tz = _extract_tz(datetime, tz)
188
+ return _make_expr("datetime_dayofyear", datetime, tz, b.Int64.ref("res"))
189
+
190
+ @classmethod
191
+ def hour(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
192
+ tz = _extract_tz(datetime, tz)
193
+ return _make_expr("datetime_hour", datetime, tz, b.Int64.ref("res"))
194
+
195
+ @classmethod
196
+ def minute(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
197
+ tz = _extract_tz(datetime, tz)
198
+ return _make_expr("datetime_minute", datetime, tz, b.Int64.ref("res"))
199
+
200
+ @classmethod
201
+ def second(cls, datetime: _DateTime) -> b.Expression:
202
+ return _make_expr("datetime_second", datetime, b.Int64.ref("res"))
203
+
204
+ @classmethod
205
+ def isoweekday(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
206
+ """
207
+ Return the ISO weekday as an integer, where Monday is 1, and Sunday is 7.
208
+ """
209
+ tz = _extract_tz(datetime, tz)
210
+ return _make_expr("datetime_weekday", datetime, tz, b.Int64.ref("res"))
211
+
212
+ @classmethod
213
+ def weekday(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
214
+ return cls.isoweekday(datetime, tz) - 1 # Convert ISO weekday (1=Mon..7=Sun) to weekday (0=Mon..6=Sun)
215
+
216
+ @classmethod
217
+ def fromordinal(cls, ordinal: _Integer) -> b.Expression:
218
+ # Convert ordinal to milliseconds, since ordinals in Python are days
219
+ # Minus 1 day since we can't declare date 0000-00-00
220
+ ordinal_milliseconds = (ordinal - 1) * 86400000 # 24 * 60 * 60 * 1000
221
+ return cls.add(b.DateTime(dt.datetime(1, 1, 1, 0, 0, 0)), milliseconds(ordinal_milliseconds))
222
+
223
+ @classmethod
224
+ def strptime(cls, date_str: _String, format: _String) -> b.Expression:
225
+ return _make_expr("parse_datetime", date_str, format, b.DateTime.ref("res"))
226
+
227
+ @classmethod
228
+ def to_date(cls, datetime: _DateTime, tz: dt.tzinfo|_String|None = None) -> b.Expression:
229
+ tz = _extract_tz(datetime, tz)
230
+ return _make_expr("construct_date_from_datetime", datetime, tz, b.Date.ref("res"))
231
+
232
+ @classmethod
233
+ def format(cls, date: _DateTime, format: _String, tz: dt.tzinfo|_String|None = None) -> b.Expression:
234
+ tz = _extract_tz(date, tz)
235
+ return _make_expr("datetime_format", date, format, tz, b.String.ref("res"))
236
+
237
+ @classmethod
238
+ def add(cls, date: _DateTime, period: b.Producer) -> b.Expression:
239
+ return _make_expr("datetime_add", date, period, b.DateTime.ref("res"))
240
+
241
+ @classmethod
242
+ def subtract(cls, date: _DateTime, period: b.Producer) -> b.Expression:
243
+ return _make_expr("datetime_subtract", date, period, b.DateTime.ref("res"))
244
+
245
+ @classmethod
246
+ def range(cls, start: _DateTime | None = None, end: _DateTime | None = None, periods: int = 1, freq: Frequency = "D") -> b.Expression:
247
+ """
248
+ Note on date_ranges and datetime_range: The way the computation works is that it first overapproximates the
249
+ number of periods.
250
+
251
+ For example, date_range(2025-02-01, 2025-03-01, freq='M') and date_range(2025-02-01, 2025-03-31, freq='M') will
252
+ compute range_end to be ceil(28*1/(365/12))=1 and ceil(58*1/(365/12))=2.
253
+
254
+ Then, the computation fetches range_end+1 items into _date, which is the right number in the first case but
255
+ one too many in the second case. That's why a filter end >= _date (or variant of) is applied, to remove any
256
+ extra item. The result is two items in both cases.
257
+ """
258
+ if start is None and end is None:
259
+ raise ValueError("Invalid start/end datetime for datetime_range. Must provide at least start datetime or end datetime")
260
+ _milliseconds = {
261
+ "ms": 1,
262
+ "s": 1 / 1_000,
263
+ "m": 1 / 60_000,
264
+ "H": 1 / 3_600_000,
265
+ "D": 1 / 86_400_000,
266
+ "W": 1 / (86_400_000 * 7),
267
+ "M": 1 / (86_400_000 * (365 / 12)),
268
+ "Y": 1 / (86_400_000 * 365),
269
+ }
270
+ date_func = cls.add
271
+ if start is None:
272
+ start = end
273
+ end = None
274
+ date_func = cls.subtract
275
+ assert start is not None
276
+ if end is not None:
277
+ num_ms = cls.period_milliseconds(start, end)
278
+ if freq == "ms":
279
+ _end = num_ms
280
+ else:
281
+ _end = std.cast(b.Int64, std.math.ceil(num_ms * _milliseconds[freq]))
282
+ # datetime_range is inclusive. add 1 since std.range is exclusive
283
+ ix = std.range(0, _end + 1, 1)
284
+ else:
285
+ ix = std.range(0, periods, 1)
286
+ _date = date_func(start, _periods[freq](ix))
287
+ if isinstance(end, dt.datetime) :
288
+ return b.DateTime(end) >= _date
289
+ elif end is not None:
290
+ return end >= _date
291
+ return _date
292
+
293
+ @classmethod
294
+ def period_milliseconds(cls, start: _DateTime, end: _DateTime) -> b.Expression:
295
+ return _make_expr("datetimes_period_milliseconds", start, end, b.Int64.ref("res"))
296
+
297
+ #--------------------------------------------------
298
+ # Periods
299
+ #--------------------------------------------------
300
+
301
+ def nanoseconds(period: _Integer) -> b.Expression:
302
+ return _make_expr("nanosecond", std.cast_to_int64(period), b.Int64.ref("res"))
303
+
304
+ def microseconds(period: _Integer) -> b.Expression:
305
+ return _make_expr("microsecond", std.cast_to_int64(period), b.Int64.ref("res"))
306
+
307
+ def milliseconds(period: _Integer) -> b.Expression:
308
+ return _make_expr("millisecond", std.cast_to_int64(period), b.Int64.ref("res"))
309
+
310
+ def seconds(period: _Integer) -> b.Expression:
311
+ return _make_expr("second", std.cast_to_int64(period), b.Int64.ref("res"))
312
+
313
+ def minutes(period: _Integer) -> b.Expression:
314
+ return _make_expr("minute", std.cast_to_int64(period), b.Int64.ref("res"))
315
+
316
+ def hours(period: _Integer) -> b.Expression:
317
+ return _make_expr("hour", std.cast_to_int64(period), b.Int64.ref("res"))
318
+
319
+ def days(period: _Integer) -> b.Expression:
320
+ return _make_expr("day", std.cast_to_int64(period), b.Int64.ref("res"))
321
+
322
+ def weeks(period: _Integer) -> b.Expression:
323
+ return _make_expr("week", std.cast_to_int64(period), b.Int64.ref("res"))
324
+
325
+ def months(period: _Integer) -> b.Expression:
326
+ return _make_expr("month", std.cast_to_int64(period), b.Int64.ref("res"))
327
+
328
+ def years(period: _Integer) -> b.Expression:
329
+ return _make_expr("year", std.cast_to_int64(period), b.Int64.ref("res"))
330
+
331
+
332
+ Frequency = Union[
333
+ Literal["ms"],
334
+ Literal["s"],
335
+ Literal["m"],
336
+ Literal["H"],
337
+ Literal["D"],
338
+ Literal["W"],
339
+ Literal["M"],
340
+ Literal["Y"],
341
+ ]
342
+
343
+ _periods = {
344
+ "ms": milliseconds,
345
+ "s": seconds,
346
+ "m": minutes,
347
+ "H": hours,
348
+ "D": days,
349
+ "W": weeks,
350
+ "M": months,
351
+ "Y": years,
352
+ }
353
+
354
+ def _extract_tz(datetime: _DateTime, tz: dt.tzinfo|_String|None) -> _String:
355
+ default_tz = "UTC"
356
+ if tz is None:
357
+ if isinstance(datetime, dt.datetime):
358
+ tz = datetime.tzname() or default_tz
359
+ else:
360
+ tz = default_tz
361
+ elif isinstance(tz, dt.tzinfo) :
362
+ tz = tz.tzname(None) or default_tz
363
+ return tz
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from relationalai.semantics.internal import internal as b
4
4
  from .std import _Number, _make_expr
5
+ from math import pi
5
6
 
6
7
  def abs(value: _Number) -> b.Expression:
7
8
  return _make_expr("abs", value, b.Number.ref("res"))
@@ -9,6 +10,17 @@ def abs(value: _Number) -> b.Expression:
9
10
  def natural_log(value: _Number) -> b.Expression:
10
11
  return _make_expr("natural_log", value, b.Number.ref("res"))
11
12
 
13
+ def log(value: _Number, base: _Number | None = None) -> b.Expression:
14
+ if base is None:
15
+ return _make_expr("natural_log", value, b.Number.ref("res"))
16
+ return _make_expr("log", value, base, b.Number.ref("res"))
17
+
18
+ def log2(value: _Number) -> b.Expression:
19
+ return _make_expr("log", value, 2, b.Number.ref("res"))
20
+
21
+ def log10(value: _Number) -> b.Expression:
22
+ return _make_expr("log10", value, b.Number.ref("res"))
23
+
12
24
  def sqrt(value: _Number) -> b.Expression:
13
25
  return _make_expr("sqrt", value, b.Float.ref("res"))
14
26
 
@@ -62,3 +74,68 @@ def asin(value: _Number) -> b.Expression:
62
74
 
63
75
  def asinh(value: _Number) -> b.Expression:
64
76
  return _make_expr("asinh", value, b.Float.ref("res"))
77
+
78
+ def tan(value: _Number) -> b.Expression:
79
+ return _make_expr("tan", value, b.Float.ref("res"))
80
+
81
+ def tanh(value: _Number) -> b.Expression:
82
+ return _make_expr("tanh", value, b.Float.ref("res"))
83
+
84
+ def atan(value: _Number) -> b.Expression:
85
+ return _make_expr("atan", value, b.Float.ref("res"))
86
+
87
+ def atanh(value: _Number) -> b.Expression:
88
+ return _make_expr("atanh", value, b.Float.ref("res"))
89
+
90
+ def cot(value: _Number) -> b.Expression:
91
+ return _make_expr("cot", value, b.Float.ref("res"))
92
+
93
+ def acot(value: _Number) -> b.Expression:
94
+ return _make_expr("acot", value, b.Float.ref("res"))
95
+
96
+ def degrees(value: _Number) -> b.Expression:
97
+ divisor = pi / 180.0
98
+ return _make_expr("/", value, divisor, b.Float.ref("res"))
99
+
100
+ def radians(value: _Number) -> b.Expression:
101
+ divisor = 180.0 / pi
102
+ return _make_expr("/", value, divisor, b.Float.ref("res"))
103
+
104
+ def exp(value: _Number) -> b.Expression:
105
+ return _make_expr("exp", value, b.Float.ref("res"))
106
+
107
+ def erf(value: _Number) -> b.Expression:
108
+ return _make_expr("erf", value, b.Float.ref("res"))
109
+
110
+ def erfinv(value: _Number) -> b.Expression:
111
+ return _make_expr("erfinv", value, b.Float.ref("res"))
112
+
113
+ def haversine(x1: _Number, y1: _Number, x2: _Number, y2: _Number, r: _Number) -> b.Expression:
114
+ # 2 * r * asin[sqrt[sin[(x2 - x1)/2] ^ 2 + cos[x1] * cos[x2] * sin[(y2 - y1) / 2] ^ 2]]
115
+ # sin[(x2 - x1)/2] ^ 2
116
+ x_diff = _make_expr("-", x2, x1, b.Float.ref("x_diff"))
117
+ x_diff2 = _make_expr("/", x_diff, 2.0, b.Float.ref("x_diff2"))
118
+ sin_x_diff = _make_expr("sin", x_diff2, b.Float.ref("sin_x_diff"))
119
+ sin_x_pow = _make_expr("pow", sin_x_diff, 2.0, b.Float.ref("sin_x_pow"))
120
+
121
+ # cos[x1] * cos[x2]
122
+ cos_x1 = _make_expr("cos", x1, b.Float.ref("cos_x1"))
123
+ cos_x2 = _make_expr("cos", x2, b.Float.ref("cos_x2"))
124
+ cos_x1_x2 = _make_expr("*", cos_x1, cos_x2, b.Float.ref("cos_x1_x2"))
125
+
126
+ # sin[(y2 - y1) / 2] ^ 2
127
+ y_diff = _make_expr("-", y2, y1, b.Float.ref("y_diff"))
128
+ y_diff2 = _make_expr("/", y_diff, 2.0, b.Float.ref("y_diff2"))
129
+ sin_y_diff = _make_expr("sin", y_diff2, b.Float.ref("sin_y_diff"))
130
+ sin_y_pow = _make_expr("pow", sin_y_diff, 2.0, b.Float.ref("sin_y_pow"))
131
+
132
+ # cos[x1] * cos[x2] * sin[(y2 - y1) / 2] ^ 2
133
+ prod = _make_expr("*", cos_x1_x2, sin_y_pow, b.Float.ref("prod"))
134
+ # sin[(x2 - x1)/2] ^ 2 + cos[x1] * cos[x2] * sin[(y2 - y1) / 2] ^ 2
135
+ prod_sum = _make_expr("+", sin_x_pow, prod, b.Float.ref("prod_sum"))
136
+
137
+ sqrt_val = _make_expr("sqrt", prod_sum, b.Float.ref("sqrt_val"))
138
+ asin_val = _make_expr("asin", sqrt_val, b.Float.ref("asin_val"))
139
+ haversine_r = _make_expr("*", r, asin_val, b.Float.ref("haversine_r"))
140
+ haversine_final = _make_expr("*", haversine_r, 2.0, b.Float.ref("haversine_final"))
141
+ return haversine_final
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ from relationalai.semantics.internal import internal as i
4
+ from relationalai.semantics.metamodel.util import OrderedSet
5
+ from .std import _Integer, _String, _make_expr
6
+ from typing import Literal, Any
7
+ from .. import std
8
+
9
+
10
+ def escape(regex: _String) -> i.Expression:
11
+ return _make_expr("escape_regex_metachars", regex, i.String.ref())
12
+
13
+ class Match(i.Producer):
14
+
15
+ def __init__(self, regex: _String, string: _String, pos: _Integer = 0, _type: Literal["search", "fullmatch", "match"] = "match"):
16
+ super().__init__(i.find_model([regex, string, pos]))
17
+ self.regex = regex
18
+ self.string = string
19
+ self.pos = pos
20
+
21
+ if _type == "match":
22
+ self._expr = _regex_match_all(self.regex, self.string, std.cast_to_int64(self.pos + 1))
23
+ self._offset, self._full_match = self._expr._arg_ref(2), self._expr._arg_ref(3)
24
+ elif _type == "search":
25
+ raise NotImplementedError("`search` is not implemented")
26
+ elif _type == "fullmatch":
27
+ _exp = _regex_match_all(self.regex, self.string, std.cast_to_int64(self.pos + 1))
28
+ self._offset, self._full_match = _exp._arg_ref(2), _exp._arg_ref(3)
29
+ self._expr = self._full_match == std.strings.substring(self.string, std.cast_to_int64(self.pos), std.strings.len(self.string))
30
+
31
+ def group(self, index: _Integer = 0) -> i.Producer:
32
+ if index == 0:
33
+ return self._full_match
34
+ else:
35
+ return _make_expr("capture_group_by_index", self.regex, self.string, std.cast_to_int64(self.pos + 1), std.cast_to_int64(index), i.String.ref("res"))
36
+
37
+ def group_by_name(self, name: _String) -> i.Producer:
38
+ return _make_expr("capture_group_by_name", self.regex, self.string, std.cast_to_int64(self.pos + 1), name, i.String.ref("res"))
39
+
40
+ def start(self) -> i.Expression:
41
+ return self._offset - 1
42
+
43
+ def end(self) -> i.Expression:
44
+ return std.strings.len(self.group(0)) + self.start() - 1
45
+
46
+ def span(self) -> tuple[i.Producer, i.Producer]:
47
+ return self.start(), self.end()
48
+
49
+ def _to_keys(self) -> OrderedSet[Any]:
50
+ return i.find_keys(self._expr)
51
+
52
+ def _compile_lookup(self, compiler:i.Compiler, ctx:i.CompilerContext):
53
+ compiler.lookup(self.regex, ctx)
54
+ compiler.lookup(self.string, ctx)
55
+ compiler.lookup(self.pos, ctx)
56
+ return compiler.lookup(self._expr, ctx)
57
+
58
+ def __getattr__(self, name: str) -> Any:
59
+ return object.__getattribute__(self, name)
60
+
61
+ def __setattr__(self, name: str, value: Any) -> None:
62
+ object.__setattr__(self, name, value)
63
+
64
+
65
+ def match(regex: _String, string: _String) -> Match:
66
+ return Match(regex, string)
67
+
68
+ def search(regex: _String, string: _String, pos: _Integer = 0) -> Match:
69
+ return Match(regex, string, pos, _type="search")
70
+
71
+ def fullmatch(regex: _String, string: _String, pos: _Integer = 0) -> Match:
72
+ return Match(regex, string, pos, _type="fullmatch")
73
+
74
+ def findall(regex: _String, string: _String) -> tuple[i.Producer, i.Producer]:
75
+ exp = _regex_match_all(regex, string)
76
+ ix, match = exp._arg_ref(2), exp._arg_ref(3)
77
+ rank = i.rank(i.asc(ix, match))
78
+ return rank, match
79
+
80
+ def _regex_match_all(regex: _String, string: _String, pos: _Integer|None = None) -> i.Expression:
81
+ if pos is None:
82
+ pos = i.Int64.ref()
83
+ return _make_expr("regex_match_all", regex, string, pos, i.String.ref())
@@ -15,7 +15,7 @@ def concat(s0: _String, s1: _String, *args: _String) -> b.Expression:
15
15
  return res
16
16
 
17
17
  def len(s: _String) -> b.Expression:
18
- return _make_expr("num_chars", s, b.Int128.ref("res"))
18
+ return _make_expr("num_chars", s, b.Int64.ref("res"))
19
19
 
20
20
  def startswith(s0: _String, s1: _String) -> b.Expression:
21
21
  return _make_expr("starts_with", s0, s1)