Module: Kumi::Core::IR::ExecutionEngine::Combinators

Defined in:
lib/kumi/core/ir/execution_engine/combinators.rb

Overview

Pure combinators for data transformation

Class Method Summary collapse

Class Method Details

.align_to(tgt, src, to_scope:, require_unique: false, on_missing: :error) ⇒ Hash

Prefix-index alignment for rank expansion/broadcasting

Parameters:

  • tgt (Hash)

    target vector (defines output structure)

  • src (Hash)

    source vector (values to align)

  • to_scope (Array)

    target scope

  • require_unique (Boolean) (defaults to: false)

    enforce unique prefixes

  • on_missing (Symbol) (defaults to: :error)

    :error or :nil policy

Returns:

  • (Hash)

    aligned vector



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/kumi/core/ir/execution_engine/combinators.rb', line 52

def self.align_to(tgt, src, to_scope:, require_unique: false, on_missing: :error)
  unless [tgt, src].all? { |v| v[:k] == :vec && v[:has_idx] }
    raise Kumi::Core::Errors::CompilerBug, "align_to expects vecs with indices"
  end

  to_rank = to_scope.length
  src_rank = src[:rows].first[:idx].length
  unless src_rank <= to_rank
    raise Kumi::Core::Errors::CompilerBug,
          "align_to: scope not prefix-compatible: #{src_rank} > #{to_rank}"
  end

  # Build prefix->value hash
  h = {}
  src[:rows].each do |r|
    k = r[:idx].first(src_rank)
    raise Kumi::Core::Errors::CompilerBug, "align_to: non-unique prefix #{k.inspect}" if require_unique && h.key?(k)

    h[k] = r[:v]
  end

  # Map target rows through alignment
  rows = tgt[:rows].map do |r|
    k = r[:idx].first(src_rank)
    if h.key?(k)
      { v: h[k], idx: r[:idx] }
    else
      case on_missing
      when :nil then { v: nil, idx: r[:idx] }
      when :error then raise Kumi::Core::Errors::RuntimeError, "align_to: no source row for prefix #{k.inspect}"
      else raise Kumi::Core::Errors::CompilerBug, "align_to: unknown on_missing policy #{on_missing.inspect}"
      end
    end
  end

  Values.vec(to_scope, rows, true)
end

.broadcast_scalar(s, v) ⇒ Hash

Broadcast scalar over vec (scalar→vec only)

Parameters:

  • s (Hash)

    scalar value => :scalar, :v => value

  • v (Hash)

    vector value => :vec, :scope => […], :rows => […]

Returns:

  • (Hash)

    broadcasted vector

Raises:



13
14
15
16
17
18
19
20
21
22
# File 'lib/kumi/core/ir/execution_engine/combinators.rb', line 13

def self.broadcast_scalar(s, v)
  raise Kumi::Core::Errors::CompilerBug, "broadcast_scalar: first arg must be scalar" unless s[:k] == :scalar
  raise Kumi::Core::Errors::CompilerBug, "broadcast_scalar: second arg must be vec" unless v[:k] == :vec

  rows = v[:rows].map do |r|
    r.key?(:idx) ? { v: s[:v], idx: r[:idx] } : { v: s[:v] }
  end

  Values.vec(v[:scope], rows, v[:has_idx])
end

.group_rows(rows, depth = 0) ⇒ Array

Build hierarchical groups for lift operation rows: [{ v: …, idx: [i0,i1,…] }, …] with lexicographically sorted :idx

Parameters:

  • rows (Array<Hash>)

    rows with indices

  • depth (Integer) (defaults to: 0)

    nesting depth

Returns:

  • (Array)

    nested array structure

Raises:

  • (ArgumentError)


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/kumi/core/ir/execution_engine/combinators.rb', line 95

def self.group_rows(rows, depth = 0)
  return [] if rows.empty?
  raise ArgumentError, "depth < 0" if depth < 0

  if depth == 0
    return rows.first[:v] if rows.first[:idx].nil? || rows.first[:idx].empty?

    return rows.map { |r| r[:v] }
  end

  out = []
  i = 0
  n = rows.length
  while i < n
    head = rows[i][:idx].first
    j = i + 1
    j += 1 while j < n && rows[j][:idx].first == head

    tail = rows[i...j].map { |r| { v: r[:v], idx: r[:idx][1..-1] } }
    out << group_rows(tail, depth - 1)
    i = j
  end
  out
end

.zip_same_scope(*vecs) ⇒ Hash

Positional zip for same-scope vecs

Parameters:

  • vecs (Array<Hash>)

    vectors to zip together

Returns:

  • (Hash)

    zipped vector

Raises:

  • (bug)


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/kumi/core/ir/execution_engine/combinators.rb', line 27

def self.zip_same_scope(*vecs)
  bug = Kumi::Core::Errors::CompilerBug
  raise bug, "zip_same_scope: all arguments must be vecs" unless vecs.all? { |v| v[:k] == :vec }
  raise bug, "zip_same_scope: all vecs must share a scope" unless vecs.map { |v| v[:scope] }.uniq.size == 1
  raise bug, "zip_same_scope: all vecs must have same row count" unless vecs.map { |v| v[:rows].size }.uniq.size == 1
  return vecs.first if vecs.length == 1

  first_vec = vecs.first
  zipped_rows = first_vec[:rows].zip(*vecs[1..].map { |v| v[:rows] }).map do |row_group|
    combined_values = row_group.map { |r| r[:v] }
    result_row = { v: combined_values }
    result_row[:idx] = row_group.first[:idx] if row_group.first.key?(:idx)
    result_row
  end

  Values.vec(first_vec[:scope], zipped_rows, first_vec[:has_idx])
end