-
Notifications
You must be signed in to change notification settings - Fork 1
developping vector #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| from operator import mul, add | ||
| from functools import reduce | ||
|
|
||
| from sympy.core import Basic | ||
| from sympy.core import Mul, Add | ||
|
|
||
| from sympde.calculus.core import BasicOperator | ||
| from symla.kronecker import Kron, LinearOperator, Vector | ||
|
|
||
| #============================================================================== | ||
| class Dot(BasicOperator): | ||
| """ | ||
| """ | ||
| def __new__(cls, *args, **options): | ||
| # (Try to) sympify args first | ||
|
|
||
| if options.pop('evaluate', True): | ||
| r = cls.eval(*args) | ||
| else: | ||
| r = None | ||
|
|
||
| if r is None: | ||
| return Basic.__new__(cls, *args, **options) | ||
| else: | ||
| return r | ||
|
|
||
| @classmethod | ||
| def eval(cls, *_args): | ||
|
|
||
| if not _args: | ||
| return | ||
|
|
||
| if not len(_args) == 2: | ||
| raise ValueError('Expecting one argument') | ||
|
|
||
| args = _args | ||
| args = list(args) | ||
| originals = args.copy() | ||
|
|
||
| # ... | ||
|
|
||
| # ... treate the case where there is and Mul node | ||
| mul_args = [arg for arg in args if isinstance(arg, Mul) and | ||
| not all([isinstance(i, LinearOperator) for i in arg.args])] | ||
| if mul_args: | ||
| arg = mul_args[0] | ||
| index = originals.index(arg) | ||
|
|
||
| linops = [i for i in arg.args if isinstance(i, (LinearOperator, Kron, Vector, Add))] | ||
| coeffs = [i for i in arg.args if i not in linops] | ||
|
|
||
| linop = reduce(mul, linops) | ||
| if len(coeffs) > 0: | ||
| coeff = reduce(mul, coeffs) | ||
| else: | ||
| coeff = 1 | ||
|
|
||
| newargs = originals.copy() | ||
| newargs[index] = linop | ||
| return coeff*cls(*newargs) | ||
| # ... | ||
|
|
||
| add_args = [arg for arg in args if isinstance(arg, Add)] | ||
| if add_args: | ||
| arg = add_args[0] | ||
| index = originals.index(arg) | ||
|
|
||
| _args = [] | ||
| for i in arg.args: | ||
| newargs = originals.copy() | ||
| newargs[index] = i | ||
| newexpr = cls(*newargs) | ||
| _args.append(newexpr) | ||
|
|
||
| return reduce(add, _args) | ||
|
|
||
| return cls(*args, evaluate=False) | ||
|
|
||
| def _hashable_content(self): | ||
| return tuple(sorted(self._args, key= lambda x:x.__hash__())) | ||
|
|
||
| def __hash__(self): | ||
| h = self._mhash | ||
| if h is None: | ||
| h = hash((type(self).__name__,)) | ||
| return h | ||
|
|
||
| def _sympystr(self, printer): | ||
| sstr = printer.doprint | ||
| return f'Dot({self.args[0]}, {self.args[1]})' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,12 @@ | ||
| from operator import mul, add | ||
| from functools import reduce | ||
|
|
||
| from sympy import Indexed, sympify, Symbol | ||
| from sympy import Symbol | ||
| from sympy import Matrix as sp_Matrix | ||
| from sympy import ImmutableDenseMatrix | ||
| from sympy import cacheit | ||
| from sympy.core import Basic | ||
| from sympy.core import Add, Mul, Pow, Expr | ||
| from sympy.core.containers import Tuple | ||
| from sympy.core.singleton import S | ||
| from sympy.core.decorators import call_highest_priority | ||
| from sympy.core.compatibility import is_sequence | ||
| from sympy.core import Add, Mul, Expr | ||
|
|
||
| from sympde.calculus.core import BasicOperator | ||
|
|
||
|
|
@@ -33,11 +29,12 @@ def is_zero(x): | |
| class FiniteVectorSpace(Basic): | ||
| """ | ||
| """ | ||
| def __new__(cls, name, shape=None): | ||
| def __new__(cls, name, dimension=None, field=None): | ||
|
|
||
| obj = Basic.__new__(cls) | ||
| obj._name = name | ||
| obj._shape = shape | ||
| obj._dimension = dimension | ||
| obj._field = field | ||
|
|
||
| return obj | ||
|
|
||
|
|
@@ -46,8 +43,17 @@ def name(self): | |
| return self._name | ||
|
|
||
| @property | ||
| def shape(self): | ||
| return self._shape | ||
| def dimension(self): | ||
| return self._dimension | ||
|
|
||
| @property | ||
| def field(self): | ||
| return self._field | ||
|
|
||
| @property | ||
| def dtype(self): | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For code generation, the data type can be infered from the field of the vector space if this information is not provided |
||
| # TODO assign a data type for each field | ||
| pass | ||
|
|
||
| def _sympystr(self, printer): | ||
| sstr = printer.doprint | ||
|
|
@@ -57,12 +63,31 @@ def __mul__(self, other): | |
| raise NotImplementedError('TODO') | ||
|
|
||
| def __hash__(self): | ||
| return hash((self.name, self.shape)) | ||
| return hash((self.name, self._dimension, self._field)) | ||
|
|
||
| #============================================================================== | ||
| # TODO improve | ||
| class Vector(Symbol): | ||
| pass | ||
| def __new__(cls, name, space=None, **assumptions): | ||
| obj = Symbol.__new__(cls, name) | ||
| obj._space = space | ||
| obj._discretizable = False | ||
| if assumptions.get('values'): | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this the way to go to discretize vector? |
||
| obj._values = assumptions.get('values') | ||
| # TODO validate values type and shape | ||
| obj._discretizable = True | ||
|
|
||
| return obj | ||
|
|
||
| @property | ||
| def shape(self): | ||
| return None if self._space == None else self._space.dimension() | ||
|
|
||
| @property | ||
| def dtype(self): | ||
| return None if self._space == None else self._space.dtype() | ||
|
|
||
|
|
||
|
|
||
| #============================================================================== | ||
| class LinearOperator(Symbol): | ||
|
|
@@ -111,8 +136,9 @@ class Kron(BasicOperator): | |
|
|
||
| def __new__(cls, *args, **options): | ||
| # (Try to) sympify args first | ||
|
|
||
| if options.pop('evaluate', True): | ||
| args = [arg.expand() if hasattr(arg, 'expand') else arg | ||
| for arg in args] | ||
| r = cls.eval(*args) | ||
| else: | ||
| r = None | ||
|
|
@@ -169,12 +195,13 @@ def eval(cls, *_args): | |
| # ... | ||
|
|
||
| # ... treate the case where there is and Mul node | ||
| mul_args = [arg for arg in args if isinstance(arg, Mul)] | ||
| mul_args = [arg for arg in args if isinstance(arg, Mul) and | ||
| not all([isinstance(i, LinearOperator) for i in arg.args])] | ||
| if mul_args: | ||
| arg = mul_args[0] | ||
| index = originals.index(arg) | ||
|
|
||
| linops = [i for i in arg.args if isinstance(i, LinearOperator)] | ||
| linops = [i for i in arg.args if isinstance(i, (LinearOperator, Kron))] | ||
| coeffs = [i for i in arg.args if i not in linops] | ||
|
|
||
| linop = reduce(mul, linops) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| from operator import mul | ||
| from functools import reduce | ||
|
|
||
| from sympy.core import Basic | ||
| from sympy.core import Add, Mul, Pow | ||
|
|
||
| from sympde.calculus.core import BasicOperator | ||
| from symla.kronecker import Kron, LinearOperator | ||
|
|
||
| #============================================================================== | ||
| class Transpose(BasicOperator): | ||
| """ | ||
| """ | ||
| def __new__(cls, *args, **options): | ||
| # (Try to) sympify args first | ||
|
|
||
| if options.pop('evaluate', True): | ||
| args = [arg.expand() if hasattr(arg, 'expand') else arg | ||
| for arg in args] | ||
| r = cls.eval(*args) | ||
| else: | ||
| r = None | ||
|
|
||
| if r is None: | ||
| return Basic.__new__(cls, *args, **options) | ||
| else: | ||
| return r | ||
|
|
||
| @classmethod | ||
| def eval(cls, *_args): | ||
|
|
||
| if not _args: | ||
| return | ||
| if not len(_args) == 1: | ||
| raise ValueError('Expecting one argument') | ||
|
|
||
| expr = _args[0] | ||
|
|
||
| if isinstance(expr, Transpose): | ||
| return _args[0].args[0] | ||
|
|
||
| if isinstance(expr, Kron): | ||
| args = [cls(a, evaluate=False) for a in expr.args] | ||
| return Kron(*args) | ||
|
|
||
| elif isinstance(expr, Pow): | ||
| return cls(expr.base) ** expr.exp | ||
|
|
||
| elif isinstance(expr, Add): | ||
| return reduce(Add, [cls(arg) for arg in expr.args]) | ||
|
|
||
| elif isinstance(expr, Mul): | ||
| linops = [i for i in expr.args if isinstance(i, (LinearOperator, Kron, Transpose))] | ||
| coeffs = [i for i in expr.args if i not in linops] | ||
|
|
||
| linops = [cls(i) for i in linops] | ||
| linop = reduce(mul, linops[::-1]) | ||
| if len(coeffs) > 0: | ||
| coeff = reduce(mul, coeffs) | ||
| else: | ||
| coeff = 1 | ||
|
|
||
| return coeff * linop | ||
|
|
||
| return cls(expr, evaluate=False) | ||
|
|
||
| def _sympystr(self, printer): | ||
| sstr = printer.doprint | ||
| return 'Trans({arg})'.format(arg=sstr(self.args[0])) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| from sympy import Symbol | ||
|
|
||
| from symla.kronecker import FiniteVectorSpace | ||
| from symla.kronecker import Matrix | ||
| from symla.kronecker import Vector | ||
| from symla.dot import Dot | ||
| from symla.transpose import Transpose | ||
|
|
||
| # ==================================================================== | ||
| def test_dot_1(): | ||
| V = FiniteVectorSpace('V') | ||
| u, v, w = [Vector(c,V) for c in ['u','v', 'w']] | ||
| alpha, beta, gamma = [Symbol(i) for i in ['alpha', 'beta', 'gamma']] | ||
| # ..................................... | ||
| assert(Dot(alpha*v,beta*u) == alpha*beta*Dot(v,u)) | ||
| assert(Dot(alpha*(v+w),beta*u) == alpha*beta*(Dot(v,u) + Dot(w,u))) | ||
| assert(Dot(u,v) == Dot(v,u)) | ||
| assert(Dot(u,v) - Dot(v,u) == 0) | ||
| assert(Dot(u,v) + Dot(v,u) == 2*Dot(v, u)) | ||
| assert(Dot(u,u) +2*Dot(u,v) + Dot(v,v) == Dot(u+v,u+v)) | ||
|
|
||
|
|
||
| ####################################### | ||
| if __name__ == '__main__': | ||
|
|
||
| test_dot_1() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we inform the dimension and field of a vector space? or should we inject that information later?
I think dimension should be used here instead of shape as it is the case in Psydac.