<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 This post continue series about AXON and pyaxon. Now we consider some examples of object serialization/deserialization.
<!-- TEASER_END -->
Step2:  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.
Step3:  Bellow we define Graph, Node, Edge classes.
Step4:  Then we define and register reduce/factory for dumping/loading.
Step5:  Now we can load AXON message with graph definition into Graph object and dump it.
Step6:  Numpy arrays
Below we consider example of transformation of numpy array objects to/from AXON text. Let's define and register reduce/factory functions.
<ASSISTANT_TASK:>
Python Code:
from __future__ import print_function, unicode_literals
from axon.api import loads, dumps
from IPython.display import HTML, display
Explanation: This post continue series about AXON and pyaxon. Now we consider some examples of object serialization/deserialization.
<!-- TEASER_END -->
End of explanation
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}
    ]
}
Explanation: 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.
End of explanation
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
Explanation: Bellow we define Graph, Node, Edge classes.
End of explanation
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])
Explanation: Then we define and register reduce/factory for dumping/loading.
End of explanation
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))
Explanation: Now we can load AXON message with graph definition into Graph object and dump it.
End of explanation
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)
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]
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)
Explanation: Numpy arrays
Below we consider example of transformation of numpy array objects to/from AXON text. Let's define and register reduce/factory functions.
End of explanation
<END_TASK>