cjdrake / pyeda

Python EDA
BSD 2-Clause "Simplified" License
304 stars 54 forks source link

Feature Request: Pickle support for ExprNode #139

Open SergiyKolesnikov opened 7 years ago

SergiyKolesnikov commented 7 years ago

Currently it is impossible to save a pyeda expression in a file using pickle:

test_input = expr('A & B & C')
with open('tmp_test.pickle', 'wb') as pickle_file:
    pickle.dump(test_input, pickle_file)
      1 test_input = expr('A & B & C')
      2 with open('tmp_test.pickle', 'wb') as pickle_file:
----> 3    pickle.dump(test_input, pickle_file)

PicklingError: Can't pickle <class 'exprnode.ExprNode'>: import of module 'exprnode' failed

For long running computations it is often useful to save intermediate results using pickle.

Meanwhile, I use DIMACS export to store expressions to a file and restore them:

import pyeda.inter as eda
import pickle

test_expr = eda.expr('A & B | C')
test_file_pathname = 'tmp_test.pickle'

def dump_expression(expression, file_pathname):
    variables_map, expression_dimacs = eda.expr2dimacscnf(expression.to_cnf())
    index_map = {}
    for dimacs_var, var in variables_map.items():
        if isinstance(dimacs_var, int) and dimacs_var >= 0:
            index_map[dimacs_var] = var.name # is ambiguous if var is an indexed variable
    with open(file_pathname, 'wb') as pickle_file:
        pickle.dump((index_map, str(expression_dimacs)), pickle_file)

def load_expression(file_pathname):
    with open(file_pathname, 'rb') as pickle_file:
        index_map, expression_dimacs_string = pickle.load(pickle_file)
    expression_indexed = eda.ast2expr(eda.parse_cnf(expression_dimacs_string))
    indexed_variables_map = {}
    for var in expression_indexed.inputs:
        var_name = index_map[var.indices[0]]
        indexed_variables_map[var] = eda.expr(var_name)
    expression = expression_indexed.compose(indexed_variables_map)
    return expression

dump_expression(test_expr, test_file_pathname)
test_expr_loaded = load_expression(test_file_pathname)

test_expr_loaded.equivalent(test_expr)
cjdrake commented 7 years ago

Have you tried the to_ast method, and the ast2expr functions?

It's not the same as pickling, but it does effectively serialize an expression.

Probably the reason there's a problem here is the boolexpr C extension. Pickle must be missing something, and I haven't done any research on it.

SergiyKolesnikov commented 7 years ago

I am getting a TypeError when I try to convert an AST back to an expression (pyeda 0.28.0):

import pyeda.inter as eda
my_expr = eda.expr('A => B')
print(my_expr)
my_expr_ast = my_expr.to_ast()
print(my_expr_ast)
eda.ast2expr(my_expr_ast)

Output:

Implies(A, B)
('impl', ('lit', 1), ('lit', 2))

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-4e111d968d2f> in <module>()
      4 my_expr_ast = my_expr.to_ast()
      5 print(my_expr_ast)
----> 6 eda.ast2expr(my_expr_ast)

/anaconda3/lib/python3.5/site-packages/pyeda/boolalg/expr.py in ast2expr(ast)
    235         return exprvar(ast[1], ast[2])
    236     else:
--> 237         xs = [ast2expr(x) for x in ast[1:]]
    238         return ASTOPS[ast[0]](*xs, simplify=False)
    239 

/anaconda3/lib/python3.5/site-packages/pyeda/boolalg/expr.py in <listcomp>(.0)
    235         return exprvar(ast[1], ast[2])
    236     else:
--> 237         xs = [ast2expr(x) for x in ast[1:]]
    238         return ASTOPS[ast[0]](*xs, simplify=False)
    239 

/anaconda3/lib/python3.5/site-packages/pyeda/boolalg/expr.py in ast2expr(ast)
    235         return exprvar(ast[1], ast[2])
    236     else:
--> 237         xs = [ast2expr(x) for x in ast[1:]]
    238         return ASTOPS[ast[0]](*xs, simplify=False)
    239 

/anaconda3/lib/python3.5/site-packages/pyeda/boolalg/expr.py in <listcomp>(.0)
    235         return exprvar(ast[1], ast[2])
    236     else:
--> 237         xs = [ast2expr(x) for x in ast[1:]]
    238         return ASTOPS[ast[0]](*xs, simplify=False)
    239 

/anaconda3/lib/python3.5/site-packages/pyeda/boolalg/expr.py in ast2expr(ast)
    230 def ast2expr(ast):
    231     """Convert an abstract syntax tree to an Expression."""
--> 232     if ast[0] == 'const':
    233         return _CONSTS[ast[1]]
    234     elif ast[0] == 'var':

TypeError: 'int' object is not subscriptable