Skip to content

Commit

Permalink
FIX: sympy 1.1 compatibility (#108)
Browse files Browse the repository at this point in the history
* FIX: Remove NumPyPrinter due to sympy 1.1 incompatibility.
* Other sympy 1.1 import changes.
* Fixes gh-105.
* FIX: Improve tests that check warnings. Warning testing is independent of the number of warnings raised and depends instead on specific string fragments.
* FIX: Switch to C89CodePrinter for sympy >= 1.1 to fix a bug with 'infinity' on MSVC compilers
  • Loading branch information
richardotis committed Jul 31, 2017
1 parent b63820b commit 57103aa
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 162 deletions.
9 changes: 6 additions & 3 deletions pycalphad/core/custom_autowrap.py
Expand Up @@ -66,14 +66,17 @@
"""
from __future__ import print_function
from sympy.core.compatibility import iterable
try:
from sympy.utilities.codegen import CCodePrinter
except ImportError:
from sympy.printing.ccode import C89CodePrinter as CCodePrinter
from sympy.utilities.codegen import (AssignmentError, OutputArgument, ResultBase,
Result, CodeGenArgumentListError,
CCodePrinter, CCodeGen, Variable)
CCodeGen, Variable)
from sympy.utilities.lambdify import implemented_function
from sympy.utilities.autowrap import CythonCodeWrapper, DummyWrapper
from subprocess import STDOUT, CalledProcessError
from subprocess import STDOUT, CalledProcessError, check_output
from sympy.printing.ccode import ccode
from sympy.core.compatibility import check_output
import os
import sys
import tempfile
Expand Down
92 changes: 0 additions & 92 deletions pycalphad/core/utils.py
Expand Up @@ -22,95 +22,6 @@ def broadcast_to(arr, shape):
return np.broadcast_arrays(arr, np.empty(shape, dtype=np.bool))[0]


class NumPyPrinter(LambdaPrinter):
"""
Numpy printer which handles vectorized piecewise functions,
logical operators, etc.
"""
_default_settings = {
"order": "none",
"full_prec": "auto",
}

def _print_seq(self, seq, delimiter=', '):
"General sequence printer: converts to tuple"
# Print tuples here instead of lists because numba supports
# tuples in nopython mode.
return '({},)'.format(delimiter.join(self._print(item) for item in seq))

#def _print_Integer(self, expr):
# # Upcast to work around an integer overflow bug
# return 'asarray({0}, dtype=float)'.format(expr)

def _print_MatrixBase(self, expr):
return "%s(%s)" % ('array', self._print(expr.tolist()))

_print_SparseMatrix = \
_print_MutableSparseMatrix = \
_print_ImmutableSparseMatrix = \
_print_Matrix = \
_print_DenseMatrix = \
_print_MutableDenseMatrix = \
_print_ImmutableMatrix = \
_print_ImmutableDenseMatrix = \
_print_MatrixBase

def _print_MatMul(self, expr):
"Matrix multiplication printer"
return '({0})'.format(').dot('.join(self._print(i) for i in expr.args))

def _print_Piecewise(self, expr):
"Piecewise function printer"
exprs = [self._print(arg.expr) for arg in expr.args]
conds = [self._print(arg.cond) for arg in expr.args]
if (len(conds) == 1) and exprs[0] == '0':
return '0'
# We expect exprs and conds to be in order here
# First condition to evaluate to True is returned
# Default result: float zero
result = 'where({0}, {1}, 0.)'.format(conds[-1], exprs[-1])
for expr, cond in reversed(list(zip(exprs[:-1], conds[:-1]))):
result = 'where({0}, {1}, {2})'.format(cond, expr, result)

return result

def _print_And(self, expr):
"Logical And printer"
# We have to override LambdaPrinter because it uses Python 'and' keyword.
# If LambdaPrinter didn't define it, we could use StrPrinter's
# version of the function and add 'logical_and' to NUMPY_TRANSLATIONS.
if len(expr.args) == 1:
return self._print(expr.args[0])
result = 'logical_and({0}, {1})'.format(self._print(expr.args[-2]), self._print(expr.args[-1]))
for arg in reversed(expr.args[:-2]):
result = 'logical_and({0}, {1})'.format(self._print(arg), result)
return result

def _print_Or(self, expr):
"Logical Or printer"
# We have to override LambdaPrinter because it uses Python 'or' keyword.
# If LambdaPrinter didn't define it, we could use StrPrinter's
# version of the function and add 'logical_or' to NUMPY_TRANSLATIONS.
if len(expr.args) == 1:
return self._print(expr.args[0])
result = 'logical_or({0}, {1})'.format(self._print(expr.args[-2]), self._print(expr.args[-1]))
for arg in reversed(expr.args[:-2]):
result = 'logical_or({0}, {1})'.format(self._print(arg), result)
return result

def _print_Not(self, expr):
"Logical Not printer"
# We have to override LambdaPrinter because it uses Python 'not' keyword.
# If LambdaPrinter didn't define it, we would still have to define our
# own because StrPrinter doesn't define it.
if len(expr.args) == 1:
return self._print(expr.args[0])
result = 'logical_not({0}, {1})'.format(self._print(expr.args[-2]), self._print(expr.args[-1]))
for arg in reversed(expr.args[:-2]):
result = 'logical_not({0}, {1})'.format(self._print(arg), result)
return result


def point_sample(comp_count, pdof=10):
"""
Sample 'pdof * (sum(comp_count) - len(comp_count))' points in
Expand Down Expand Up @@ -185,9 +96,6 @@ def make_callable(model, variables, mode=None):

if mode == 'sympy':
energy = lambda *vs: model.subs(zip(variables, vs)).evalf()
elif mode == 'numpy':
energy = lambdify(tuple(variables), model, dummify=True,
modules=[{'where': np.where, 'Abs': np.abs}, 'numpy'], printer=NumPyPrinter)
else:
energy = lambdify(tuple(variables), model, dummify=True,
modules=mode)
Expand Down
73 changes: 6 additions & 67 deletions pycalphad/tests/test_energy.py
Expand Up @@ -21,7 +21,7 @@ def test_sympify_safety():
_sympify_string(teststr) # should throw ParseException


def calculate_energy(model, variables, mode='numpy'):
def calculate_energy(model, variables, mode='sympy'):
"""
Calculate the value of the energy at a point.
Expand All @@ -43,7 +43,7 @@ def calculate_energy(model, variables, mode='numpy'):
# Unpack all the values in the dict and use them to call the function
return energy(*(list(variables.values())))

def check_energy(model, variables, known_value, mode='numpy'):
def check_energy(model, variables, known_value, mode='sympy'):
"Check that our calculated energy matches the known value."
desired = calculate_energy(model, variables, mode)
known_value = np.array(known_value, dtype=np.complex)
Expand All @@ -65,20 +65,6 @@ def test_pure_sympy():
v.SiteFraction('L12_FCC', 1, 'AL'): 1}, \
-3.01732e4, mode='sympy')

def test_pure_numpy():
"Pure component end-members in numpy mode."
check_energy(Model(DBF, ['AL'], 'LIQUID'), \
{v.T: 2000, v.SiteFraction('LIQUID', 0, 'AL'): 1}, \
-1.28565e5, mode='numpy')
check_energy(Model(DBF, ['AL'], 'B2'), \
{v.T: 1400, v.SiteFraction('B2', 0, 'AL'): 1,
v.SiteFraction('B2', 1, 'AL'): 1}, \
-6.57639e4, mode='numpy')
check_energy(Model(DBF, ['AL'], 'L12_FCC'), \
{v.T: 800, v.SiteFraction('L12_FCC', 0, 'AL'): 1,
v.SiteFraction('L12_FCC', 1, 'AL'): 1}, \
-3.01732e4, mode='numpy')

def test_degenerate_ordered():
"Degenerate sublattice configuration has same energy as disordered phase."
mod_l12 = Model(DBF, ['CR', 'NI'], 'L12_FCC')
Expand Down Expand Up @@ -114,12 +100,6 @@ def test_binary_magnetic():
v.SiteFraction('L12_FCC', 1, 'CR'): 0.33,
v.SiteFraction('L12_FCC', 1, 'NI'): 0.67}, \
-1.68840e4, mode='sympy')
check_energy(Model(DBF, ['CR', 'NI'], 'L12_FCC'), \
{v.T: 500, v.SiteFraction('L12_FCC', 0, 'CR'): 0.33,
v.SiteFraction('L12_FCC', 0, 'NI'): 0.67,
v.SiteFraction('L12_FCC', 1, 'CR'): 0.33,
v.SiteFraction('L12_FCC', 1, 'NI'): 0.67}, \
-1.68840e4, mode='numpy')

def test_binary_magnetic_reimported():
"Export and re-import a TDB before the calculation."
Expand All @@ -129,7 +109,7 @@ def test_binary_magnetic_reimported():
v.SiteFraction('L12_FCC', 0, 'NI'): 0.67,
v.SiteFraction('L12_FCC', 1, 'CR'): 0.33,
v.SiteFraction('L12_FCC', 1, 'NI'): 0.67},
-1.68840e4, mode='numpy')
-1.68840e4, mode='sympy')

def test_binary_magnetic_ordering():
"Two-component phase with IHJ magnetic model and ordering."
Expand All @@ -140,23 +120,13 @@ def test_binary_magnetic_ordering():
v.SiteFraction('L12_FCC', 1, 'CR'): 9.33965e-1,
v.SiteFraction('L12_FCC', 1, 'NI'): 6.60348e-2}, \
-9.23953e3, mode='sympy')
check_energy(Model(DBF, ['CR', 'NI'], 'L12_FCC'), \
{v.T: 300, v.SiteFraction('L12_FCC', 0, 'CR'): 4.86783e-2,
v.SiteFraction('L12_FCC', 0, 'NI'): 9.51322e-1,
v.SiteFraction('L12_FCC', 1, 'CR'): 9.33965e-1,
v.SiteFraction('L12_FCC', 1, 'NI'): 6.60348e-2}, \
-9.23953e3, mode='numpy')

def test_binary_dilute():
"Dilute binary solution phase."
check_energy(Model(DBF, ['CR', 'NI'], 'LIQUID'), \
{v.T: 300, v.SiteFraction('LIQUID', 0, 'CR'): 1e-12,
v.SiteFraction('LIQUID', 0, 'NI'): 1.0-1e-12}, \
5.52773e3, mode='sympy')
check_energy(Model(DBF, ['CR', 'NI'], 'LIQUID'), \
{v.T: 300, v.SiteFraction('LIQUID', 0, 'CR'): 1e-12,
v.SiteFraction('LIQUID', 0, 'NI'): 1.0-1e-12}, \
5.52773e3, mode='numpy')

def test_binary_xiong_twostate_einstein():
"Phase with Xiong magnetic, two-state and Einstein energy contributions."
Expand All @@ -165,15 +135,15 @@ def test_binary_xiong_twostate_einstein():
check_energy(mod, {v.T: 10, v.SiteFraction('LIQUID', 0, 'FE'): 1,
v.SiteFraction('LIQUID', 0, 'MN'): 0,
v.SiteFraction('LIQUID', 1, 'VA'): 1},
10158.591, mode='numpy')
10158.591, mode='sympy')
check_energy(mod, {v.T: 300, v.SiteFraction('LIQUID', 0, 'FE'): 0.3,
v.SiteFraction('LIQUID', 0, 'MN'): 0.7,
v.SiteFraction('LIQUID', 1, 'VA'): 1},
4200.8435, mode='numpy')
4200.8435, mode='sympy')
check_energy(mod, {v.T: 1500, v.SiteFraction('LIQUID', 0, 'FE'): 0.8,
v.SiteFraction('LIQUID', 0, 'MN'): 0.2,
v.SiteFraction('LIQUID', 1, 'VA'): 1},
-86332.217, mode='numpy')
-86332.217, mode='sympy')

# TERNARY TESTS
def test_ternary_rkm_solution():
Expand All @@ -183,11 +153,6 @@ def test_ternary_rkm_solution():
v.SiteFraction('LIQUID', 0, 'CR'): 0.20,
v.SiteFraction('LIQUID', 0, 'NI'): 0.36}, \
-1.16529e5, mode='sympy')
check_energy(Model(DBF, ['AL', 'CR', 'NI'], 'LIQUID'), \
{v.T: 1500, v.SiteFraction('LIQUID', 0, 'AL'): 0.44,
v.SiteFraction('LIQUID', 0, 'CR'): 0.20,
v.SiteFraction('LIQUID', 0, 'NI'): 0.36}, \
-1.16529e5, mode='numpy')

def test_ternary_symmetric_param():
"Generate the other two ternary parameters if only the zeroth is specified."
Expand All @@ -208,14 +173,6 @@ def test_ternary_ordered_magnetic():
v.SiteFraction('L12_FCC', 1, 'CR'): 2.50002e-1,
v.SiteFraction('L12_FCC', 1, 'NI'): 4.55313e-10}, \
-40717.204, mode='sympy')
check_energy(Model(DBF, ['AL', 'CR', 'NI'], 'L12_FCC'), \
{v.T: 300, v.SiteFraction('L12_FCC', 0, 'AL'): 5.42883e-8,
v.SiteFraction('L12_FCC', 0, 'CR'): 2.07934e-6,
v.SiteFraction('L12_FCC', 0, 'NI'): 9.99998e-1,
v.SiteFraction('L12_FCC', 1, 'AL'): 7.49998e-1,
v.SiteFraction('L12_FCC', 1, 'CR'): 2.50002e-1,
v.SiteFraction('L12_FCC', 1, 'NI'): 4.55313e-10}, \
-40717.204, mode='numpy')

# QUATERNARY TESTS
def test_quaternary():
Expand All @@ -230,16 +187,6 @@ def test_quaternary():
v.SiteFraction('B2', 1, 'NI'): 5.03467e-1,
v.SiteFraction('B2', 1, 'VA'): 1e-12}, \
-42368.27, mode='sympy')
check_energy(Model(DBF, ['AL', 'CR', 'NI', 'VA'], 'B2'), \
{v.T: 500, v.SiteFraction('B2', 0, 'AL'): 4.03399e-9,
v.SiteFraction('B2', 0, 'CR'): 2.65798e-4,
v.SiteFraction('B2', 0, 'NI'): 9.99734e-1,
v.SiteFraction('B2', 0, 'VA'): 2.68374e-9,
v.SiteFraction('B2', 1, 'AL'): 3.75801e-1,
v.SiteFraction('B2', 1, 'CR'): 1.20732e-1,
v.SiteFraction('B2', 1, 'NI'): 5.03467e-1,
v.SiteFraction('B2', 1, 'VA'): 1e-12}, \
-42368.27, mode='numpy')

# SPECIAL CASES
def test_case_sensitivity():
Expand All @@ -248,21 +195,13 @@ def test_case_sensitivity():
{v.T: 300, v.SiteFraction('LIQUID', 0, 'CR'): 1e-12,
v.SiteFraction('liquid', 0, 'ni'): 1}, \
5.52773e3, mode='sympy')
check_energy(Model(DBF, ['Cr', 'nI'], 'Liquid'), \
{v.T: 300, v.SiteFraction('LIQUID', 0, 'CR'): 1e-12,
v.SiteFraction('liquid', 0, 'ni'): 1}, \
5.52773e3, mode='numpy')

def test_zero_site_fraction():
"Energy of a binary solution phase where one site fraction is zero."
check_energy(Model(DBF, ['CR', 'NI'], 'LIQUID'), \
{v.T: 300, v.SiteFraction('LIQUID', 0, 'CR'): 0,
v.SiteFraction('LIQUID', 0, 'NI'): 1}, \
5.52773e3, mode='sympy')
check_energy(Model(DBF, ['CR', 'NI'], 'LIQUID'), \
{v.T: 300, v.SiteFraction('LIQUID', 0, 'CR'): 0,
v.SiteFraction('LIQUID', 0, 'NI'): 1}, \
5.52773e3, mode='numpy')

def test_invalid_arguments_energy_zero():
"Undefined symbols in CompiledModel are set to zero (gh-54)."
Expand Down

0 comments on commit 57103aa

Please sign in to comment.