Multiply MultiIndexSet
Instances#
import minterpy as mp
import numpy as np
This guide demonstrates how to multiply instances of MultiIndexSet
and shows the convention adopted by Minterpy regarding the product of two multi-indices.
The following three cases are considered:
MultiIndexSet
instances with the same dimensionMultiIndexSet
instances with different dimensionsMultiIndexSet
instances with different \(l_p\)-degrees
The multiplication between MultiIndexSet
instances may be carried out via a method call (MultiIndexSet.multiply()
) or an operator (*
or *=
).
Instances with the same dimension#
As a motivating example, consider the following two multi-index sets of the same dimension:
Both sets are defined with respect to the same \(l_p\)-degree of \(1.0\). Notice that both sets are downward-closed.
mi_a = mp.MultiIndexSet(
np.array([[0, 0], [1, 0], [2, 0], [0, 1]]),
lp_degree=1.0,
)
mi_b = mp.MultiIndexSet(
np.array([[0, 0], [0, 1], [0, 2], [0, 3]]),
lp_degree=1.0,
)
The product of these two sets can be constructed using the multiplication (*
) operator:
mi_prod_1 = mi_a * mi_b
print(mi_prod_1)
MultiIndexSet(m=2, n=5, p=1.0)
[[0 0]
[1 0]
[2 0]
[0 1]
[1 1]
[2 1]
[0 2]
[1 2]
[2 2]
[0 3]
[1 3]
[2 3]
[0 4]]
The dimension of the product set remains the same as that of the operands (in this case, \(2\)). Also notice that the product set is lexicographically sorted. Furthermore, because the operands are downward-closed, the resulting product remains downward-closed. That is:
mi_prod_1.is_downward_closed
True
The polynomial degree of the product set is:
print(mi_prod_1.poly_degree)
5
Note
If the \(l_p\)-degree of both operands is \(1.0\) then the polynomial degree of the product set is the sum of the polynomial degrees of the operands.
print((mi_a.poly_degree, mi_b.poly_degree))
(2, 3)
Alternatively, the union may also be created using a method call whose result is equivalent:
mi_prod_1 == mi_a.multiply(mi_b)
True
The multi-index sets do not have to be downward-closed for taking the product. Consider, for instance, a multi-index set that is not downward-closed:
again with respect to \(l_p\)-degree of \(1.0\).
mi_c = mp.MultiIndexSet(
np.array([[1, 0], [0, 3]]),
lp_degree=1.0,
)
The product with the multi-index set \(A\) is:
mi_a * mi_c
MultiIndexSet(
array([[1, 0],
[2, 0],
[3, 0],
[1, 1],
[0, 3],
[1, 3],
[2, 3],
[0, 4]]),
lp_degree=1.0
)
Because one of the operands is not downward-closed, the product set is also not downward-closed. That is:
(mi_a * mi_c).is_downward_closed
False
Instances with different dimensions#
Multi-index sets of different dimension may also be multiplied. For instance, consider the following:
Both sets are defined with the same \(l_p\)-degree of \(1.0\).
mi_d = mp.MultiIndexSet(
np.array([[0, 0], [1, 0]]),
lp_degree=1.0,
)
mi_e = mp.MultiIndexSet(
np.array([[0, 0, 0], [1, 0, 0], [0, 0, 1]]),
lp_degree=1.0,
)
The product set is:
mi_prod_2 = mi_d * mi_e
print(mi_prod_2)
MultiIndexSet(m=3, n=2, p=1.0)
[[0 0 0]
[1 0 0]
[2 0 0]
[0 0 1]
[1 0 1]]
Notice that the product set has the same dimension as the largest dimension of the operands (in this case, \(3\)).
The polynomial degree of the product set is:
print(mi_prod_2.poly_degree)
2
Instances with different \(l_p\)-degrees#
If two multi-index sets with different \(l_p\)-degrees are multiplied, then the \(l_p\)-degrees of the product set is the maximum of the \(l_p\)-degrees of the operands. Consider the following example:
mi_f = mp.MultiIndexSet(
np.array([[0, 0], [1, 0], [1, 0], [1, 1]]),
lp_degree=np.inf,
)
mi_g = mp.MultiIndexSet(
np.array([[0, 0], [1, 0], [0, 1]]),
lp_degree=1.0,
)
The product set is:
mi_prod_3 = mi_f * mi_g
print(mi_prod_3)
MultiIndexSet(m=2, n=2, p=inf)
[[0 0]
[1 0]
[2 0]
[0 1]
[1 1]
[2 1]
[1 2]]
By convention, the \(l_p\)-degree of the product set is the maximum of the \(l_p\)-degrees of the operands (in this case, \(\infty\)):
print(mi_prod_3.lp_degree)
inf
The polynomial degree of the product set is computed based on the resulting index set and \(l_p\)-degree:
print(mi_prod_3.poly_degree)
2
In-place multiplication#
The product of two MultiIndexSet
instances may also be created in-place. That is, one of the instance is directly updated by the product.
To create the product via an in-place method call, set the inplace
parameter to True
(the default is set to False
):
mi_a.multiply(mi_b, inplace=True)
The instance mi_a
has been updated in-place:
mi_a == mi_prod_1
True
Alternatively, to create the product via an inplace operator:
mi_d *= mi_e
Similarly as before, the instance mi_d
has been updated in-place:
mi_d == mi_prod_2
True