Module: GRX::Serialization

Defined in:
lib/grx/serialization.rb

Overview

=================================================================== Serialization — Native binary .grx format

Binary layout:

  • 8 bytes: Magic header "GRX1\0\0\0\0"
  • 4 bytes: Number of parameter tensors (unsigned 32-bit big-endian) For each tensor:
    • 2 bytes: Rank (number of dimensions)
    • 4 bytes * rank: Dimensions of the shape (uint32 big-endian)
    • 8 bytes: Total numel (uint64 big-endian)
    • numel * 8 bytes: Direct binary packed IEEE 754 doubles

===================================================================

Constant Summary collapse

MAGIC =
"GRX1\x00\x00\x00\x00".b

Class Method Summary collapse

Class Method Details

.load(model, path) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/grx/serialization.rb', line 41

def self.load(model, path)
  params = model.parameters
  File.open(path, "rb") do |f|
    magic = f.read(8)
    raise StorageError, "Invalid format: not a valid .grx binary file" unless magic == MAGIC
    count = f.read(4).unpack1("N")
    raise StorageError, "Parameter count mismatch: model has #{params.size}, file has #{count}" unless count == params.size

    params.each_with_index do |p, idx|
      rank = f.read(2).unpack1("n")
      shape = f.read(rank * 4).unpack("N*")
      numel = f.read(8).unpack1("Q>")
      raise ShapeError, "Shape mismatch for parameter #{idx}: expected #{p.shape}, got #{shape}" unless shape == p.shape

      bytes = f.read(numel * 8)
      if p.storage.ptr
        p.storage.ptr[0, bytes.bytesize] = bytes
      else
        p.storage.instance_variable_set(:@data, bytes.unpack("d*"))
      end
    end
  end
  model
end

.save(model, path) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/grx/serialization.rb', line 19

def self.save(model, path)
  params = model.parameters
  File.open(path, "wb") do |f|
    f.write(MAGIC)
    f.write([params.size].pack("N"))
    params.each do |p|
      shape = p.shape
      f.write([shape.size].pack("n"))
      f.write(shape.pack("N*"))
      f.write([p.numel].pack("Q>"))
      # Direct binary copy from native C buffer
      bytes = if p.storage.ptr
        p.storage.ptr[0, p.numel * 8]
      else
        p.to_a.pack("d*")
      end
      f.write(bytes)
    end
  end
  path
end