This post continue series about AXON and pyaxon. Now we consider some examples of object serialization/deserialization.


In [1]:
from __future__ import print_function, unicode_literals
from axon.api import loads, dumps
from IPython.display import HTML, display

Simple graph example

Below is AXON text that represents a graph by the way of definition of sequences of nodes and edges. Each node has a reference label and each edge specifies left and right nodes by its reference labels. Using native support of references in AXON makes such representation straightforward.


In [2]:
text = """
graph {
    nodes: [
        &1 node {x:1 y:1}
        &2 node {x:1 y:2}
        &3 node {x:2 y:2}
    ]
    edges: [
        edge {*1 *2}
        edge {*1 *3}
        edge {*2 *3}
    ]
}
"""

Bellow we define Graph, Node, Edge classes.


In [3]:
class Base(object):
    #
    def __str__(self):
        return '%s: %r' % (self.__class__.__name__, self.__dict__)
    #
    __repr__ = __str__

class Graph(Base):
    def __init__(self, nodes=None, edges=None):
        self.nodes = list(nodes) if nodes else []
        self.edges = list(edges) if edges else []

class Node(Base):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
class Edge(Base):
    def __init__(self, p1, p2):
        self.left = p1
        self.right = p2

Then we define and register reduce/factory for dumping/loading.


In [4]:
from axon.utils import factory, reduce
import axon

@factory('graph')
def create_graph(attrs, args):
    return Graph(**attrs)

@factory('node')
def create_node(attrs, args):
    return Node(**attrs)

@factory('edge')
def create_edge(attrs, args):
    return Edge(*args)

@reduce(Graph)
def reduce_graph(graph):
    return axon.node('graph', {'nodes': graph.nodes, 'edges': graph.edges})

@reduce(Node)
def reduce_node(node):
    return axon.node('node', {'x': node.x, 'y': node.y})

@reduce(Edge)
def reduce_edge(edge):
    return axon.node('edge', None, [edge.left, edge.right])

Now we can load AXON message with graph definition into Graph object and dump it.


In [5]:
g = loads(text, mode='strict')
display(HTML(u'<b>Graph object:</b>'))
print(g[0])
display(HTML(u'<b>Compact dump:</b>'))
print(dumps(g, crossref=1))
display(HTML(u'<b>Formatted dump without braces:</b>'))
print(dumps(g, pretty=1, crossref=1, hsize=4))
display(HTML(u'<b>Formatted dump with braces:</b>'))
print(dumps(g, pretty=1, braces=1, crossref=1, hsize=4))


Graph object:
Graph: {'nodes': [Node: {'y': 1, 'x': 1}, Node: {'y': 2, 'x': 1}, Node: {'y': 2, 'x': 2}], 'edges': [Edge: {'left': Node: {'y': 1, 'x': 1}, 'right': Node: {'y': 2, 'x': 1}}, Edge: {'left': Node: {'y': 1, 'x': 1}, 'right': Node: {'y': 2, 'x': 2}}, Edge: {'left': Node: {'y': 2, 'x': 1}, 'right': Node: {'y': 2, 'x': 2}}]}
Compact dump:
graph{nodes:[&3 node{x:1 y:1} &1 node{x:1 y:2} &2 node{x:2 y:2}] edges:[edge{*3 *1} edge{*3 *2} edge{*1 *2}]}
Formatted dump without braces:
graph
  nodes: [
    &3 node
      x: 1
      y: 1
    &1 node
      x: 1
      y: 2
    &2 node
      x: 2
      y: 2]
  edges: [
    edge
      *3 *1
    edge
      *3 *2
    edge
      *1 *2]
Formatted dump with braces:
graph {
  nodes: [
    &3 node {
      x: 1
      y: 1}
    &1 node {
      x: 1
      y: 2}
    &2 node {
      x: 2
      y: 2}]
  edges: [
    edge {*3 *1}
    edge {*3 *2}
    edge {*1 *2}]}

Numpy arrays

Below we consider example of transformation of numpy array objects to/from AXON text. Let's define and register reduce/factory functions.


In [6]:
from axon import dump_as_str, as_unicode, factory, reduce
import numpy as np

@factory('ndarray')
def create_array(mapping, sequence):
    shape = mapping.get('shape', None)
    dtype = mapping['dtype']
    if type(dtype) is list:
        dtype = [(str(n), str(t)) for n, t in dtype]
    a = np.array(sequence, dtype=dtype)
    if shape is not None:
        a.shape = shape
    return a

@reduce(np.ndarray)
def reduce_array(a):
    signes = {'<', '=', '>', '!'}
    if len(a.dtype.descr) > 1:
        dtype = [
            (as_unicode(n), (as_unicode(t[1:]) \
                             if t[0] in signes \
                             else as_unicode(t)))
            for n, t in a.dtype.descr]
        return axon.node('ndarray', {'dtype':dtype}, a.tolist())
    else:
        dtype_str = a.dtype.str
        dtype_str = as_unicode(dtype_str[1:]) \
                if dtype_str[0] in signes \
                else as_unicode(dtype_str)
        return axon.node('ndarray', {'shape': a.shape, 'dtype':as_unicode(dtype_str)}, a.tolist())

dump_as_str(np.int8)
dump_as_str(np.int16)
dump_as_str(np.int32)
dump_as_str(np.int64)
dump_as_str(np.float16)
dump_as_str(np.float32)
dump_as_str(np.float64)
dump_as_str(np.float128)
dump_as_str(np.int_)
dump_as_str(np.float_)
dump_as_str(np.double)

In [7]:
a = np.array([[1, 2, 3], [3, 4, 5], [5, 6, 7]])
display(HTML('<b>Compact form:</b>'))
text = dumps([a])
print(text)

b = loads(text, mode="strict")[0]
display(HTML('<b>Formatted form with braces:</b>'))
text = dumps([a], pretty=1, braces=1, hsize=4)
print(text)

display(HTML('<b>Formatted form with braces:</b>'))
text = dumps([a], pretty=1, hsize=4)
print(text)
b = loads(text, mode="strict")[0]


Compact form:
ndarray{shape:(3 3) dtype:"i8" [1 2 3] [3 4 5] [5 6 7]}
Formatted form with braces:
ndarray {
  shape: (3 3)
  dtype: "i8"
  [1 2 3]
  [3 4 5]
  [5 6 7]}
Formatted form with braces:
ndarray
  shape: (3 3)
  dtype: "i8"
  [1 2 3]
  [3 4 5]
  [5 6 7]

In [8]:
a = np.array(
        [(1, 2, 3.0), (3, 4, 5.0), (4, 5, 6.0)], 
        dtype=[('x', int), ('y', int), ('z', float)])
text = dumps([a])
print('val=', text)
b = loads(text, mode="strict")[0]
print('val=', repr(b))

display(HTML('<b>Formatted form</b>:'))
text = dumps([a])
print('val=', text)
display(HTML('<b>Formatted form</b>:'))
text = dumps([a], pretty=1, braces=1, hsize=3)
print('val=', text)
display(HTML('<b>Indented form:</b>'))
text = dumps([a], pretty=1, hsize=3)
print('val=', text)


val= ndarray{dtype:[("x" "i8") ("y" "i8") ("z" "f8")] (1 2 3.0) (3 4 5.0) (4 5 6.0)}
val= array([(1, 2, 3.0), (3, 4, 5.0), (4, 5, 6.0)], 
      dtype=[('x', '<i8'), ('y', '<i8'), ('z', '<f8')])
Formatted form:
val= ndarray{dtype:[("x" "i8") ("y" "i8") ("z" "f8")] (1 2 3.0) (3 4 5.0) (4 5 6.0)}
Formatted form:
val= ndarray {
  dtype: [
    ("x" "i8")
    ("y" "i8")
    ("z" "f8")]
  (1 2 3.0)
  (3 4 5.0)
  (4 5 6.0)}
Indented form:
val= ndarray
  dtype: [
    ("x" "i8")
    ("y" "i8")
    ("z" "f8")]
  (1 2 3.0)
  (3 4 5.0)
  (4 5 6.0)

In [ ]: