From 110c90944f0f0aa9d9d3ff3a2edf6d9620520f04 Mon Sep 17 00:00:00 2001 From: Markus Holzer <markus.holzer@fau.de> Date: Thu, 16 Dec 2021 16:42:37 +0100 Subject: [PATCH] Fix constraint check --- pystencils/kernelcreation.py | 49 +++++--------- pystencils/node_collection.py | 53 ++++++++++++--- pystencils/simp/assignment_collection.py | 30 +-------- pystencils/typing/utilities.py | 7 +- .../test_size_and_layout_checks.py | 19 ++++-- .../test_small_block_benchmark.ipynb | 67 ++++++++++--------- 6 files changed, 116 insertions(+), 109 deletions(-) diff --git a/pystencils/kernelcreation.py b/pystencils/kernelcreation.py index 0f943c5ea..9c2198a81 100644 --- a/pystencils/kernelcreation.py +++ b/pystencils/kernelcreation.py @@ -1,5 +1,4 @@ import itertools -import logging import warnings from typing import Union, List @@ -65,14 +64,18 @@ def create_kernel(assignments: Union[Assignment, List[Assignment], AssignmentCol assignments = [assignments] assert assignments, "Assignments must not be empty!" if isinstance(assignments, list): - if all((isinstance(a, Assignment) for a in assignments)): - assignments = AssignmentCollection(assignments) - elif all((isinstance(n, Node) for n in assignments)): - assignments = NodeCollection(assignments) - logging.warning('Using Nodes is experimental and not fully tested. Double check your generated code!') - else: - raise ValueError(f'The list "{assignments}" is mixed. Pass either a list of "pystencils.Assignments" ' - f'or a list of "pystencils.astnodes.Node') + assignments = NodeCollection(assignments) + elif isinstance(assignments, AssignmentCollection): + # TODO check and doku + # --- applying first default simplifications + try: + if config.default_assignment_simplifications: + simplification = create_simplification_strategy() + assignments = simplification(assignments) + except Exception as e: + warnings.warn(f"It was not possible to apply the default pystencils optimisations to the " + f"AssignmentCollection due to the following problem :{e}") + assignments = NodeCollection(assignments.all_assignments) if config.index_fields: return create_indexed_kernel(assignments, config=config) @@ -80,7 +83,7 @@ def create_kernel(assignments: Union[Assignment, List[Assignment], AssignmentCol return create_domain_kernel(assignments, config=config) -def create_domain_kernel(assignments: Union[AssignmentCollection, NodeCollection], *, config: CreateKernelConfig): +def create_domain_kernel(assignments: NodeCollection, *, config: CreateKernelConfig): """ Creates abstract syntax tree (AST) of kernel, using a list of update equations. @@ -96,10 +99,11 @@ def create_domain_kernel(assignments: Union[AssignmentCollection, NodeCollection >>> import pystencils as ps >>> import numpy as np >>> from pystencils.kernelcreation import create_domain_kernel + >>> from pystencils.node_collection import NodeCollection >>> s, d = ps.fields('s, d: [2D]') >>> assignment = ps.Assignment(d[0,0], s[0, 1] + s[0, -1] + s[1, 0] + s[-1, 0]) >>> kernel_config = ps.CreateKernelConfig(cpu_openmp=True) - >>> kernel_ast = create_domain_kernel(ps.AssignmentCollection([assignment]), config=kernel_config) + >>> kernel_ast = create_domain_kernel(NodeCollection([assignment]), config=kernel_config) >>> kernel = kernel_ast.compile() >>> d_arr = np.zeros([5, 5]) >>> kernel(d=d_arr, s=np.ones([5, 5])) @@ -110,21 +114,8 @@ def create_domain_kernel(assignments: Union[AssignmentCollection, NodeCollection [0., 4., 4., 4., 0.], [0., 0., 0., 0., 0.]]) """ - - # --- applying first default simplifications - if isinstance(assignments, AssignmentCollection): - try: - if config.default_assignment_simplifications and isinstance(assignments, AssignmentCollection): - simplification = create_simplification_strategy() - assignments = simplification(assignments) - except Exception as e: - warnings.warn(f"It was not possible to apply the default pystencils optimisations to the " - f"AssignmentCollection due to the following problem :{e}") - - assignments.evaluate_terms() - # --- eval - # TODO split apply_sympy_optimisations and do the eval here + assignments.evaluate_terms() # FUTURE WORK from here we shouldn't NEED sympy # --- check constrains @@ -132,12 +123,8 @@ def create_domain_kernel(assignments: Union[AssignmentCollection, NodeCollection check_double_write_condition=not config.allow_double_writes) check.visit(assignments) - if isinstance(assignments, AssignmentCollection): - assert assignments.bound_fields == check.fields_written, f'WTF' - assert assignments.rhs_fields == check.fields_read, f'WTF' - else: - assignments.bound_fields = check.fields_written - assignments.rhs_fields = check.fields_read + assignments.bound_fields = check.fields_written + assignments.rhs_fields = check.fields_read # ---- Creating ast ast = None diff --git a/pystencils/node_collection.py b/pystencils/node_collection.py index 545f30149..68f53f36c 100644 --- a/pystencils/node_collection.py +++ b/pystencils/node_collection.py @@ -1,15 +1,50 @@ -from typing import List +import logging +from typing import List, Union + +import sympy as sp +from sympy.codegen import Assignment +from sympy.codegen.rewriting import ReplaceOptim, optimize + from pystencils.astnodes import Node +from pystencils.functions import DivFunc + -# TODO ABC for NodeCollection and AssignmentCollection class NodeCollection: - def __init__(self, nodes: List[Node]): - self.nodes = nodes - self.bound_fields = None - self.rhs_fields = None + def __init__(self, assignments: List[Union[Node, Assignment]]): + self.all_assignments = assignments + + if all((isinstance(a, Assignment) for a in assignments)): + self.is_Nodes = False + self.is_Assignments = True + elif all((isinstance(n, Node) for n in assignments)): + self.is_Nodes = True + self.is_Assignments = False + logging.warning('Using Nodes is experimental and not fully tested. Double check your generated code!') + else: + raise ValueError(f'The list "{assignments}" is mixed. Pass either a list of "pystencils.Assignments" ' + f'or a list of "pystencils.astnodes.Node') + self.simplification_hints = () - @property - def all_assignments(self): - return self.nodes + def evaluate_terms(self): + + # There is no visitor implemented now so working with nodes does not work + if self.is_Nodes: + return + + evaluate_constant_terms = ReplaceOptim( + lambda e: hasattr(e, 'is_constant') and e.is_constant and not e.is_integer, + lambda p: p.evalf()) + + evaluate_pow = ReplaceOptim( + lambda e: e.is_Pow and e.exp.is_Integer and abs(e.exp) <= 8, + lambda p: ( + sp.UnevaluatedExpr(sp.Mul(*([p.base] * +p.exp), evaluate=False)) if p.exp > 0 else + DivFunc(sp.Integer(1), sp.Mul(*([p.base] * -p.exp), evaluate=False)) + )) + + sympy_optimisations = [evaluate_constant_terms, evaluate_pow] + self.all_assignments = [Assignment(a.lhs, optimize(a.rhs, sympy_optimisations)) + if hasattr(a, 'lhs') + else a for a in self.all_assignments] diff --git a/pystencils/simp/assignment_collection.py b/pystencils/simp/assignment_collection.py index 5a6f0d010..69dcf9567 100644 --- a/pystencils/simp/assignment_collection.py +++ b/pystencils/simp/assignment_collection.py @@ -3,11 +3,9 @@ from copy import copy from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence, Set, Union import sympy as sp -from sympy.codegen.rewriting import ReplaceOptim, optimize import pystencils from pystencils.assignment import Assignment -from pystencils.functions import DivFunc from pystencils.simp.simplifications import (sort_assignments_topologically, transform_lhs_and_rhs, transform_rhs) from pystencils.sympyextensions import count_operations, fast_subs @@ -341,8 +339,10 @@ class AssignmentCollection: new_eqs = [Assignment(eq.lhs, fast_subs(eq.rhs, subs_dict)) for eq in self.main_assignments] return self.copy(new_eqs, new_subexpressions) - def new_without_subexpressions(self, subexpressions_to_keep: Set[sp.Symbol] = set()) -> 'AssignmentCollection': + def new_without_subexpressions(self, subexpressions_to_keep=None) -> 'AssignmentCollection': """Returns a new collection where all subexpressions have been inserted.""" + if subexpressions_to_keep is None: + subexpressions_to_keep = set() if len(self.subexpressions) == 0: return self.copy() @@ -365,30 +365,6 @@ class AssignmentCollection: new_assignment = [fast_subs(eq, substitution_dict) for eq in self.main_assignments] return self.copy(new_assignment, kept_subexpressions) - - def evaluate_terms(self): - - evaluate_constant_terms = ReplaceOptim( - lambda e: hasattr(e, 'is_constant') and e.is_constant and not e.is_integer, - lambda p: p.evalf()) - - evaluate_pow = ReplaceOptim( - lambda e: e.is_Pow and e.exp.is_Integer and abs(e.exp) <= 8, - lambda p: ( - sp.UnevaluatedExpr(sp.Mul(*([p.base] * +p.exp), evaluate=False)) if p.exp > 0 else - DivFunc(sp.Integer(1), sp.Mul(*([p.base] * -p.exp), evaluate=False)) - )) - - sympy_optimisations = [evaluate_constant_terms, evaluate_pow] - - self.subexpressions = [Assignment(a.lhs, optimize(a.rhs, sympy_optimisations)) - if hasattr(a, 'lhs') - else a for a in self.subexpressions] - - self.main_assignments = [Assignment(a.lhs, optimize(a.rhs, sympy_optimisations)) - if hasattr(a, 'lhs') - else a for a in self.main_assignments] - # ----------------------------------------- Display and Printing ------------------------------------------------- def _repr_html_(self): diff --git a/pystencils/typing/utilities.py b/pystencils/typing/utilities.py index 821a5d227..1cc62c168 100644 --- a/pystencils/typing/utilities.py +++ b/pystencils/typing/utilities.py @@ -177,12 +177,7 @@ def get_type_of_expression(expr, else: forbid_collation_to_complex = expr.is_real is True forbid_collation_to_float = expr.is_integer is True - return collate_types( - types, - forbid_collation_to_complex=forbid_collation_to_complex, - forbid_collation_to_float=forbid_collation_to_float, - default_float_type=default_float_type, - default_int_type=default_int_type) + return collate_types(types) else: if expr.is_integer: return create_type(default_int_type) diff --git a/pystencils_tests/test_size_and_layout_checks.py b/pystencils_tests/test_size_and_layout_checks.py index 27696e19f..08b747f74 100644 --- a/pystencils_tests/test_size_and_layout_checks.py +++ b/pystencils_tests/test_size_and_layout_checks.py @@ -1,5 +1,7 @@ import numpy as np import pytest + +import pystencils import sympy as sp from pystencils import Assignment, Field, create_kernel, fields @@ -104,13 +106,20 @@ def test_loop_independence_checks(): Assignment(g[0, 0], f[1, 0])]) assert 'Field g is written at two different locations' in str(e.value) - # This is allowed - because only one element of g is accessed + # This is not allowed - because this is not SSA (it can be overwritten with allow_double_writes) + with pytest.raises(ValueError) as e: + create_kernel([Assignment(g[0, 2], f[0, 1]), + Assignment(g[0, 2], 2 * g[0, 2])]) + + # This is allowed - because allow_double_writes is True now create_kernel([Assignment(g[0, 2], f[0, 1]), - Assignment(g[0, 2], 2 * g[0, 2])]) + Assignment(g[0, 2], 2 * g[0, 2])], + config=pystencils.CreateKernelConfig(allow_double_writes=True)) - create_kernel([Assignment(v[0, 2](1), f[0, 1]), - Assignment(v[0, 1](0), 4), - Assignment(v[0, 2](1), 2 * v[0, 2](1))]) + with pytest.raises(ValueError) as e: + create_kernel([Assignment(v[0, 2](1), f[0, 1]), + Assignment(v[0, 1](0), 4), + Assignment(v[0, 2](1), 2 * v[0, 2](1))]) with pytest.raises(ValueError) as e: create_kernel([Assignment(g[0, 1], 3), diff --git a/pystencils_tests/test_small_block_benchmark.ipynb b/pystencils_tests/test_small_block_benchmark.ipynb index 81101c5a0..24d815bde 100644 --- a/pystencils_tests/test_small_block_benchmark.ipynb +++ b/pystencils_tests/test_small_block_benchmark.ipynb @@ -2,9 +2,20 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "<module 'waLBerla' from '/Users/holzer/walberla/python/waLBerla/__init__.py'>" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import pytest\n", "pytest.importorskip('waLBerla')" @@ -12,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -31,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -44,7 +55,7 @@ "[2, 4, 8, 16, 32, 64, 128]" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -58,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -105,20 +116,27 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Computing size 2\n", - "Computing size 4\n", - "Computing size 8\n", - "Computing size 16\n", - "Computing size 32\n", - "Computing size 64\n", - "Computing size 128\n" + "Computing size 2\n" + ] + }, + { + "ename": "ValueError", + "evalue": "Cannot create parallel data handling because walberla module is not available", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/var/folders/07/0d7kq8fd0sx24cs53zz90_qc0000gp/T/ipykernel_12649/2009975470.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mname_to_func\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mouter_repeats\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mtime\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 10\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'block_size'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'name'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/var/folders/07/0d7kq8fd0sx24cs53zz90_qc0000gp/T/ipykernel_12649/3509370390.py\u001b[0m in \u001b[0;36mbenchmark_datahandling\u001b[0;34m(domain_size, parallel)\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mbenchmark_datahandling\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdomain_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparallel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 22\u001b[0;31m \u001b[0mdh\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mps\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_data_handling\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdomain_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparallel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mparallel\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 23\u001b[0m \u001b[0mf_src\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_array\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'src'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mf_dst\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_array\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'dst'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/pystencils/pystencils/pystencils/datahandling/__init__.py\u001b[0m in \u001b[0;36mcreate_data_handling\u001b[0;34m(domain_size, periodicity, default_layout, default_target, parallel, default_ghost_layers)\u001b[0m\n\u001b[1;32m 44\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mparallel\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mwlb\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 46\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Cannot create parallel data handling because walberla module is not available\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 47\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 48\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mperiodicity\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mFalse\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mperiodicity\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: Cannot create parallel data handling because walberla module is not available" ] } ], @@ -139,22 +157,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "<Figure size 1152x432 with 2 Axes>" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "if 'is_test_run' not in globals():\n", " import pandas as pd\n", @@ -174,7 +179,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -188,7 +193,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.2" + "version": "3.9.9" } }, "nbformat": 4, -- GitLab