Add union operation support to MultiIndexSet instances
In order to support a more consistent addition and subtraction between polynomials, MultiIndexSet
instances should support union operation via the dunder method __or__()
.
Below are the expected behaviors using some examples.
Same dimension, same lp-degree
>>> import minterpy as mp
>>> import numpy as np
>>> mi_1 = mp.MultiIndexSet.from_degree(2, 2, 1)
>>> mi_1
MultiIndexSet
[[0 0]
[1 0]
[2 0]
[0 1]
[1 1]
[0 2]]
>>> mi_2 = mp.MultiIndexSet.from_degree(2, 1, 1)
>>> mi_2
MultiIndexSet
[[0 0]
[1 0]
[0 1]]
>>> mi_3 = mi_1 | mi_2
>>> mi_3
MultiIndexSet
[[0 0]
[1 0]
[2 0]
[0 1]
[1 1]
[0 2]]
>>> mi_3.poly_degree
2
The resulting exponents are the union of exponents between the two multi-index sets. As a set, any repeated elements will be squashed in the resulting set.
The polynomial degree will be recomputed based on the resulting union of exponents and the given l_p
-degree.
Furthermore, if the two sets are complete (resp. downward-closed) then the union will also be complete (resp. downward-closed). If one of the sets is incomplete (resp. downward-open) then the union may or may not be complete (resp. downward-closed).
Same dimension, different lp-degree
>>> import minterpy as mp
>>> import numpy as np
>>> mi_1 = mp.MultiIndexSet.from_degree(2, 1, np.inf)
>>> mi_1
MultiIndexSet
[[0 0]
[1 0]
[0 1]
[1 1]]
>>> mi_2 = mp.MultiIndexSet.from_degree(2, 1, 1)
>>> mi_2
MultiIndexSet
[[0 0]
[1 0]
[0 1]]
>>> mi_3 = mi_1 | mi_2
>>> mi_3
MultiIndexSet
[[0 0]
[1 0]
[0 1]
[1 1]]
>>> mi_3.lp_degree
inf
>>> mi_3.poly_degree
2
Here, the l_p
-degree of the resulting multi-index set, by convention, should be the larger between the l_p
-degrees of the two sets (not added otherwise repeated index sets multiplication will cause the l_p
-degree to grows quickly which is rather strange).
Different dimension
>>> mi_1 = mp.MultiIndexSet.from_degree(1, 3, 1)
>>> mi_1
MultiIndexSet
[[0] # later be expanded to [0, 0]
[1] # later be expanded to [1, 0]
[2] # later be expanded to [2, 0]
[3]] # later be expanded to [3, 0]
>>> mi_2 = mp.MultiIndexSet.from_degree(2, 2, 1)
>>> mi_2
MultiIndexSet
[[0 0]
[1 0]
[2 0]
[0 1]
[1 1]
[0 2]]
>>> mi_3 = mi_1 | mi_2
MultiIndexSet
[[0 0]
[1 0]
[2 0]
[3 0]
[0 1]
[1 1]
[0 2]]
Here, the dimension of the first multi-index set (having the lower dimension) is first expanded to the dimension of the second set (having the higher dimension). Then, the same procedure as the above follows to obtain the resulting set.
Notes on the current behavior
Taking the union between instances of MultiIndexSet
is not currently supported,
but the _lagrange_add()
has an implementation of a multi-index set addition for the multiplication between two Lagrange polynomials.
However, in that implementation the resulting multi-index set of the summed polynomial is always forced to be complete by creating a new multi-index set that has the maximum of the degrees and the maximum of the l_p
-degrees.
The multi-indices constructed by those parameters will contain both of the multi-index sets of the polynomial operands.
If the operands polynomials have incomplete set, there are many exponents that are not needed in the sum.
By implementing the union of the set from the bottom up (from MultiIndexSet
) instead directly in the polynomial basis classes, we can have a more consistent and reusable behavior.
This issue is part of the larger issue of refactoring the multi-index set (Issue #99 (closed)) as well as supporting the computation of orthogonal polynomials in Minterpy (see Issue #116).