import proveit
from proveit import defaults, UnsatisfiedPrerequisites
from proveit import Function, ExprRange, ExprTuple, IndexedVar
from proveit import a, f, i, u, v, w, x, y, z, alpha, beta, fi, gamma, delta
from proveit.logic import Forall, Equals, NotEquals, InSet, CartExp
from proveit.numbers import Natural, Real, Complex
from proveit.numbers import one, two, three, four, five, Interval
from proveit.linear_algebra import (
VecSpaces, VecAdd, VecSum, VecZero, ScalarMult, TensorProd, TensorExp)
%begin demonstrations
In order to apply the tensor product theorems, we need the operands to be known as vectors in vector spaces over a common field. For convenience, we may specify a default field, or we may specify a field when calling the TensorProd methods.
Let's set some defaults for convienience in our testing below.
R3 = CartExp(Real, three)
C3 = CartExp(Complex, three)
defaults.assumptions = [
InSet(u, R3), InSet(v, R3), InSet(w, R3),
InSet(x, R3), InSet(y, R3), InSet(z, R3),
NotEquals(x, VecZero(R3)), NotEquals(z, VecZero(R3)),
InSet(a, Real), InSet(alpha, Complex), InSet(beta, Real),
InSet(gamma, Real), InSet (delta, Real),
Forall(i, InSet(fi, R3), domain=Natural)]
summand_assumption = defaults.assumptions[-1]
summand_assumption.instantiate(assumptions=[summand_assumption,
InSet(i, Natural)])
TensorProd
For Testing¶tensor_prod_00 = TensorProd(ScalarMult(a, x), y)
tensor_prod_000 = TensorProd(x, ScalarMult(a, y))
tensor_prod_01 = TensorProd(ScalarMult(a, x), y, z)
tensor_prod_02 = TensorProd(x, ScalarMult(a, y), z)
tensor_prod_03 = TensorProd(x, y, ScalarMult(a, z))
tensor_prod_04 = TensorProd(u, TensorProd(v, ScalarMult(a, w), x,
ScalarMult(alpha, y)))
tensor_prod_05 = TensorProd(u, ScalarMult(a, v), VecAdd(w, x, y), z)
tensor_prod_06 = TensorProd(VecAdd(x, y), z)
tensor_prod_07 = TensorProd(x, VecAdd(y, z))
tensor_prod_08 = TensorProd(u, v, VecAdd(w, x, y), z)
tensor_prod_with_sum_01 = TensorProd(x, VecSum(i, Function(f, i), domain=Interval(one, three)), z)
tensor_prod_with_sum_02 = TensorProd(VecSum(i, Function(f, i), domain=Interval(two, four)), z)
tensor_prod_with_sum_03 = TensorProd(x, VecSum(i, Function(f, i), domain=Interval(two, four)))
tensor_prod_with_sum_04 = TensorProd(u, v, w, x, VecSum(i, Function(f, i), domain=Interval(one, five)), z)
tensor_prod_inside_sum_01 = VecSum(i, TensorProd(x, Function(f, i)), domain=Interval(two, four))
tensor_prod_inside_sum_02 = VecSum(i, TensorProd(y, Function(f, i)), domain=Interval(two, four))
tensor_prod_inside_sum_03 = VecSum(i, TensorProd(z, Function(f, i)), domain=Interval(two, four))
tensor_prod_inside_sum_04 = VecSum(i, TensorProd(Function(f, i), x), domain=Interval(two, four))
tensor_prod_inside_sum_05 = VecSum(i, TensorProd(x, Function(f, i), z), domain=Interval(two, four))
tensor_prod_inside_sum_06 = VecSum(i, TensorProd(x, y, Function(f, i), z), domain=Interval(two, four))
Upon implementing Issue #28, or something along those lines, the following should not be necessary:
i_domains = [tensor_prod_with_sum_01.operands[1].domain,
tensor_prod_with_sum_03.operands[1].domain,
tensor_prod_with_sum_04.operands[-2].domain]
judgments = []
for i_domain in i_domains:
judgment = summand_assumption.instantiate(
assumptions=[summand_assumption, InSet(i, i_domain)])
judgments.append(judgment)
judgments
TensorProd
simplification¶help(TensorProd.shallow_simplification)
First test out unary TensorProd
simplification.
TensorProd(x).simplification()
Our next test will involve ungrouping and pulling out scalar factors but will not work without a proper default field specified since it mixes real vectors with a complex scalar.
tensor_prod_04
VecSpaces.default_field = Complex
defaults.assumptions
tensor_prod_04.simplification()
TensorProd.membership_object()
method¶x_y_z = TensorProd(x, y, z)
R3_R3_R3 = TensorProd(R3, R3, R3)
# So, this is interesting … generates an error
# VecSpaces.known_field(R3_R3_R3)
# THIS HANGS FOREVER
# this suggests it would be good to have a TensorProd.containing_vec_space() method
# but this was working before implementing the TensorProdMembership class ?!
# field=Real
# temp_vec_space = VecSpaces.known_vec_space(x_y_z, field=field)
# VecSpaces.known_field(temp_vec_space)
A manual construction of the instantiation process for the tensor_prod_is_in_tensor_prod_space
theorem from the proveit.linear_algebra.tensors
package.
from proveit.linear_algebra.tensors import tensor_prod_is_in_tensor_prod_space
tensor_prod_is_in_tensor_prod_space
temp_self = InSet(x_y_z, TensorProd(R3, R3, R3))
_a_sub = temp_self.element.operands
_i_sub = _a_sub.num_elements()
# This does NOT work, nor does get_field appear designed for this
_K_sub = VecSpaces.get_field(temp_self.domain)
# THIS HANGS!
# we could try the following, which seems unpromising given that we already
# know what vec space we should be dealing with and should be able to derive
# the correct field instead of supplying it; notice that the result is not
# even satisfactory, returning Complexes instead of obvious Reals
# temp_self.element.deduce_in_vec_space(field=None)
# try this instead:
# does not work if we omit the automation=True
domain_is_vec_space = temp_self.domain.deduce_as_vec_space(automation=True)
# followed by this:
_K_sub = VecSpaces.known_field(temp_self.domain)
vec_spaces = temp_self.domain.operands
from proveit import a, i, K, V
tensor_prod_is_in_tensor_prod_space.instantiate(
{a: _a_sub, i: _i_sub, K: _K_sub, V: vec_spaces})
InSet(x_y_z, TensorProd(R3, R3, R3)).conclude()
InSet(x_y_z, TensorProd(C3, C3, C3)).conclude()
TensorProd.association()
and TensorProd.disassociation()
methods¶help(TensorProd.association)
help(TensorProd.disassociation)
# without the automation=True, gives error
# with automation=True, just HANGS
# tensor_prod_with_sum_04.association(1, 4, automation=True)
tensor_prod_04.disassociation(1, auto_simplify=False)
TensorProd.scalar_factorization()
method¶help(TensorProd.scalar_factorization)
tensor_prod_00
tensor_prod_00.scalar_factorization(0, field=Real)
By default, the first ScalarMult operand will be the target. Also, we may use the default field, VecSpaces.default_field
.
VecSpaces.default_field = Real
tensor_prod_000.scalar_factorization()
tensor_prod_01.scalar_factorization()
tensor_prod_02.scalar_factorization(1)
tensor_prod_03.scalar_factorization()
tensor_prod_05.scalar_factorization()
TensorProd.distribution()
method¶help(TensorProd.distribution)
tensor_prod_05
tensor_prod_05.distribution(2)
tensor_prod_06
tensor_prod_06.distribution(0)
tensor_prod_07
tensor_prod_07.distribution(1)
tensor_prod_08
tensor_prod_08.distribution(2)
tensor_prod_with_sum_01
tensor_prod_with_sum_01.distribution(1)
temp_self = tensor_prod_with_sum_01
field=None
_V_sub = VecSpaces.known_vec_space(temp_self, field=field)
# notice here that _V_sub is a TensorProd, not a VecSpace
type(_V_sub)
_K = VecSpaces.known_field(_V_sub)
idx = 1
sum_factor = temp_self.operands[idx]
_a_sub = temp_self.operands[:idx]
_c_sub = temp_self.operands[idx+1:]
_i_sub = _a_sub.num_elements()
_k_sub = _c_sub.num_elements()
_b_sub = sum_factor.indices
_j_sub = _b_sub.num_elements()
from proveit import Lambda
_f_sub = Lambda(sum_factor.indices, sum_factor.summand)
_Q_sub = Lambda(sum_factor.indices, sum_factor.condition)
from proveit import K, f, Q, i, j, k, V, a, b, c
from proveit.linear_algebra.tensors import tensor_prod_distribution_over_summation
impl = tensor_prod_distribution_over_summation.instantiate(
{K:_K_sub, f:_f_sub, Q:_Q_sub, i:_i_sub, j:_j_sub, k:_k_sub,
V:_V_sub, a:_a_sub, b:_b_sub, c:_c_sub},
preserve_all=True)
tensor_prod_with_sum_02
tensor_prod_with_sum_02.distribution(0)
tensor_prod_with_sum_03
tensor_prod_with_sum_03.distribution(1)
tensor_prod_with_sum_04
tensor_prod_with_sum_04.distribution(4)
# recall one of our TensorProd objects without a sum or summation:
tensor_prod_02
# we should get a meaningful error message when trying to distribute across
# a factor that is not a sum or summation:
try:
tensor_prod_02.distribution(1)
assert False, "Expecting a ValueError; should not get this far!"
except ValueError as the_error:
print("ValueError: {}".format(the_error))
Working on details for a possible TensorProd.factoring() method below.
tensor_prod_inside_sum_01
That is actually equal to:
\begin{align}
x \otimes f(2) + x \otimes f(3) + x \otimes f(4)
&=
x \otimes (f(2) + f(3) + f(4))\\
&=
x \otimes \sum_{i=2}^{4} f(i)
\end{align}
We want to be able to call tensor_prod_inside_sum_01.factorization(idx)
, where idx
indicates the (0-based) index of the tensor product term to leave inside the summation. Clearly any term(s) that are functions of the summation index itself cannot be pulled out, and we cannot change the order of the tensor product terms. In the simple example here, we would call tensor_prod_inside_sum_01.factorization(1)
, to obtain
\begin{align}
\vdash
\sum_{i=2}^{4} \left[x \otimes f(i)\right]
&=
x \otimes \sum_{i=2}^{4} f(i)
\end{align}
from proveit.linear_algebra.tensors import tensor_prod_distribution_over_summation
tensor_prod_distribution_over_summation
self_test = tensor_prod_inside_sum_01
idx = 1
field = None
_V_sub = VecSpaces.known_vec_space(self_test, field=field)
_K_sub = VecSpaces.known_field(_V_sub)
_a_sub = self_test.summand.operands[:idx]
_c_sub = self_test.summand.operands[idx+1:]
_i_sub = _a_sub.num_elements()
_k_sub = _c_sub.num_elements()
_b_sub = self_test.indices
_j_sub = _b_sub.num_elements()
_f_sub = Lambda(self_test.indices, self_test.summand.operands[idx])
_Q_sub = Lambda(self_test.indices, self_test.condition)
tensor_prod_distribution_over_summation.instance_params
from proveit import K, f, Q, i, j, k, V, a, b, c
impl = tensor_prod_distribution_over_summation.instantiate(
{K:_K_sub, f:_f_sub, Q:_Q_sub, i:_i_sub, j:_j_sub, k:_k_sub,
V:_V_sub, a:_a_sub, b:_b_sub, c:_c_sub}, preserve_all=True)
impl.derive_consequent().derive_reversed().with_wrapping_at()
The tensor_prod_factoring()
method technically is a method called on a VecSum object, but it's good to briefly illustrate its use here in tensors, essentially performing the inverse operation of the TensorProd.distribution()
method:
# The numeric argument indicates the tensor product factor
# to LEAVE inside the VecSum
tensor_prod_inside_sum_01.tensor_prod_factoring(1)
tensor_prod_inside_sum_03
defaults.assumptions
try:
tensor_prod_inside_sum_03.tensor_prod_factoring(1)
except Exception as the_exception:
print("Exception: {}".format(the_exception))
Testing/problems for factoring the case for tensor_prod_inside_sum_03
, which is identical to tensor_prod_inside_sum_01
except now we have z instead of x and z has all the same properties as x …
self_test = tensor_prod_inside_sum_03
field = None
_V_sub = VecSpaces.known_vec_space(self_test, field=field)
_K_sub = VecSpaces.known_field(_V_sub)
_a_sub = self_test.summand.operands[:idx]
_c_sub = self_test.summand.operands[idx+1:]
_i_sub = _a_sub.num_elements()
_k_sub = _c_sub.num_elements()
_b_sub = self_test.indices
_j_sub = _b_sub.num_elements()
_f_sub = Lambda(self_test.indices, self_test.summand.operands[idx])
_Q_sub = Lambda(self_test.indices, self_test.condition)
from proveit import K, f, Q, i, j, k, V, a, b, c
impl = tensor_prod_distribution_over_summation.instantiate(
{K:_K_sub, f:_f_sub, Q:_Q_sub, i:_i_sub, j:_j_sub, k:_k_sub,
V:_V_sub, a:_a_sub, b:_b_sub, c:_c_sub}, preserve_all=True)
defaults.assumptions
InSet(z, R3).proven()
InSet(VecSum(i, Function(f, i), domain=Interval(two, four)), R3).proven()
TensorProd(z, VecSum(i, Function(f, i), domain=Interval(two, four))).deduce_in_vec_space(TensorProd(R3, R3), field=Real)
InSet(TensorProd(z, VecSum(i, Function(f, i), domain=Interval(two, four))), TensorProd(R3, R3)).prove()
impl.derive_consequent()
impl.derive_consequent().derive_reversed().with_wrapping_at()
tensor_prod_inside_sum_02.tensor_prod_factoring(1)
tensor_prod_inside_sum_03.tensor_prod_factoring(1)
tensor_prod_inside_sum_04.tensor_prod_factoring(0)
tensor_prod_inside_sum_05.tensor_prod_factoring(1)
tensor_prod_inside_sum_06.tensor_prod_factoring(2)
scalar_mult_example = ScalarMult(alpha,tensor_prod_inside_sum_06)
scalar_mult_example.inner_expr().operands[1].tensor_prod_factoring(2)
scalar_mult_example_02 = VecSum(i, TensorProd(ScalarMult(beta, x), ScalarMult(beta, y)), domain=Interval(two, four))
scalar_mult_example_03 = VecSum(i, ScalarMult(gamma, ScalarMult(beta, TensorProd(x, fi, y))), domain=Interval(two, four))
scalar_mult_example_04 = VecSum(i, ScalarMult(gamma, ScalarMult(i, TensorProd(x, y))), domain=Interval(two, four))
from proveit.numbers import Add
scalar_mult_example_05 = VecSum(
i,
ScalarMult(gamma, ScalarMult(i, ScalarMult(beta, ScalarMult(Add(i, one), TensorProd(x, y))))),
domain=Interval(two, four))
defaults.assumptions
from proveit.logic import InClass
from proveit.linear_algebra import VecSpaces
InClass(x, VecSpaces(Real))
defaults.assumptions + (InClass(fi, VecSpaces(Real)), InClass(x, VecSpaces(Real)))
# the scalar_factorization() takes out just a single scalar it finds first
# which is somewhat problematic, because it makes it difficult to then
# repeat the process because it produces a ScalarMult at the top level
scalar_mult_example_02_factored_01 = scalar_mult_example_02.summand.scalar_factorization()
scalar_mult_example_02.summand.simplification()
scalar_mult_example_02_factored_01
# we would then have to dig into the next level
# scalar_mult_example_02_factored_01.inner_expr().rhs.operands[1].factor_scalar()
scalar_mult_example_02_factored_01
# shallow simplify is working, pulling the scalars out front
# and eliminating nested ScalarMults
scalar_mult_example_02_factored_02 = scalar_mult_example_02_factored_01.inner_expr().rhs.operands[1].shallow_simplify()
# double_scaling_reduce() seems to work now
# scalar_mult_example_02_factored_02.inner_expr().rhs.double_scaling_reduce()
scalar_mult_example_02.summand.shallow_simplification(
assumptions=defaults.assumptions + (InClass(fi, VecSpaces(Real)), InClass(x, VecSpaces(Real))))
simplification_01 = scalar_mult_example_02.inner_expr().summand.shallow_simplification()
scalar_mult_example_02.tensor_prod_factoring(idx=1)
test_result = scalar_mult_example_02.factors_extraction()
from proveit.linear_algebra.scalar_multiplication import distribution_over_vec_sum
distribution_over_vec_sum
temp_self = scalar_mult_example_02
from proveit import TransRelUpdater
expr = temp_self
eq = TransRelUpdater(expr)
eq.relation
expr = eq.update(expr.inner_expr().summand.shallow_simplification())
eq.relation
expr
# this might be tricky — will known_vec_space return the same for every
# a_1, … a_i ?
_V_sub = VecSpaces.known_vec_space(expr.summand.scaled, field=None)
_K_sub = VecSpaces.known_field(_V_sub)
_b_sub = expr.indices
_j_sub = _b_sub.num_elements()
# from proveit import Lambda
_f_sub = Lambda(expr.indices, expr.summand.scaled)
_Q_sub = Lambda(expr.indices, expr.condition)
_k_sub = expr.summand.scalar
from proveit import V, K, a, b, f, j, k, Q
imp = distribution_over_vec_sum.instantiate(
{V: _V_sub, K: _K_sub, b: _b_sub, j: _j_sub,
f: _f_sub, Q: _Q_sub, k: _k_sub}, auto_simplify=False)
imp.derive_consequent().derive_reversed()
expr = eq.update(imp.derive_consequent().derive_reversed())
eq.relation
expr
expr.inner_expr().scaled.tensor_prod_factoring(1)
expr = eq.update(expr.inner_expr().scaled.tensor_prod_factoring(1))
eq.relation
scalar_mult_example_03
# scalar_mult_example_03.factors_extraction()
# consider this example now, noting the factor of index var 'i'
scalar_mult_example_04
# this seems to work ok when called literally like this:
scalar_mult_example_04.inner_expr().summand.shallow_simplification()
# but what if we try to use the TransRelUpdater?
expr = scalar_mult_example_04 # a VecSum with a ScalarMult summand
eq = TransRelUpdater(scalar_mult_example_04)
display(eq.relation)
expr = eq.update(expr.inner_expr().summand.shallow_simplification())
display(eq.relation)
# this will not yet work, because the i factor causes it to
# completely fail; want to check if the ScalarMult.scalar is itself a Mult,
# then check and pull out what we can from that
# scalar_mult_example_04.factors_extraction(assumptions=defaults.assumptions + (InSet(i, Real),))
(1) If the summand is a ScalarMult, we then check to see if the ScalarMult.scalar involves the summand index.
(2) If not, factor it out! If it does, we might have to look more carefully …
scalar_mult_example
scalar_mult_example_01 = VecSum(i, ScalarMult(gamma, TensorProd(x, y, fi, z)), domain=Interval(two, four))
Here trying to manually re-create the VecSum.deduce_in_vec_space() for when the summand is a ScalarMult of a TensorProd
# this process works for a single vector left behind in the sum
# but not when we associate and then try
expr = scalar_mult_example_01
field = None
idx = 2 # just a single idx right now
idx_beg = 1
idx_end = 2
from proveit import TransRelUpdater
eq = TransRelUpdater(expr)
eq.relation
# associate the chosen elements to remain
if idx_beg != idx_end:
expr = eq.update(expr.inner_expr().summand.scaled.association(
idx_beg, idx_end - idx_beg + 1, auto_simplify=False))
idx = idx_beg
display(expr)
display(idx)
The following cell seems important, and may need to be implemented in some way in the VecSum.tensor_prod_factoring() method for certain circumstances. Need to talk with WW about this though. This VecSpaces.known_vec_space() succeeds where the one in the next cell was continuing to be problematic.
# Now trying process without this and just the one above
####### put in some extra steps to help with the ########
####### vector field stuff?
# This is based on analogous stuff in VecSum.deduce_in_vec_space()
# with some modifications for ScalarMult summand
self = expr
with defaults.temporary() as tmp_defaults:
tmp_defaults.assumptions = (defaults.assumptions + self.conditions.entries)
vec_space = VecSpaces.known_vec_space(self.summand, field=field) #or on scaled?
_V_sub = VecSpaces.known_vec_space(self.summand, field=field)
display(vec_space)
# _V_sub = VecSpaces.known_vec_space(expr, field=field)
# display(expr)
# _V_sub = VecSpaces.known_vec_space(expr.summand)
# VecSpaces.known_vec_space(expr, field=Real)
_K_sub = VecSpaces.known_field(_V_sub)
_b_sub = expr.indices
_j_sub = _b_sub.num_elements()
_Q_sub = Lambda(expr.indices, expr.condition)
_a_sub = expr.summand.scaled.operands[:idx]
_c_sub = expr.summand.scaled.operands[idx+1:]
_s_sub = Lambda(expr.indices, expr.summand.scalar)
_f_sub = Lambda(expr.indices, expr.summand.scaled.operands[idx] )
_i_sub = _a_sub.num_elements()
_k_sub = _c_sub.num_elements()
from proveit.linear_algebra.tensors import tensor_prod_distribution_over_summation_with_scalar_mult
tensor_prod_distribution_over_summation_with_scalar_mult
from proveit import K, f, Q, i, j, k, V, a, b, c, s
impl = tensor_prod_distribution_over_summation_with_scalar_mult.instantiate(
{K:_K_sub, f:_f_sub, Q:_Q_sub, i:_i_sub, j:_j_sub,
k:_k_sub, V:_V_sub, a:_a_sub, b:_b_sub, c:_c_sub,
s: _s_sub}, auto_simplify=False)
impl.derive_consequent().derive_reversed()
tensor_prod_factoring()
¶scalar_mult_example_01.tensor_prod_factoring(idx=2)
scalar_mult_example_01.tensor_prod_factoring(idx_beg=1, idx_end=2)
scalar_mult_example_01.tensor_prod_factoring(idx_beg=1, idx_end=3)
scalar_mult_example_01.tensor_prod_factoring(idx_beg=0, idx_end=2)
scalar_mult_example_02.tensor_prod_factoring(idx=0)
scalar_mult_example_02.tensor_prod_factoring(idx=1)
# with no arguments, we factor as much as possible and reduce if possible
scalar_mult_example_02.tensor_prod_factoring()
scalar_mult_example_03
# the tensor_prod_factoring() will also work
# if the ScalarMults are nested (at least to some degree)
scalar_mult_example_03.tensor_prod_factoring(idx=1)
scalar_mult_example_03.inner_expr().tensor_prod_factoring(idx_beg=1, idx_end=2)
type(scalar_mult_example_03.summand.scaled.scaled.operands.num_elements())
temp_result = scalar_mult_example_03.inner_expr().factors_extraction(
assumptions = defaults.assumptions + scalar_mult_example_03.conditions.entries)
TensorProd.association()
for tensor products of sets¶# Some example TensorProds of CartExps
example_vec_space_01, example_vec_space_02, example_vec_space_03, example_vec_space_04, example_vec_space_05 = (
TensorProd(R3, R3, R3, R3), TensorProd(R3, TensorProd(R3, R3)), TensorProd(x, R3),
TensorProd(TensorProd(C3, TensorProd(C3, C3)), C3, C3),
TensorProd(TensorProd(R3, TensorProd(C3, y)), R3, C3))
example_vec_space_01_assoc = example_vec_space_01.association(1, 2)
# For this to work, we need to establish ahead of time that one of the
# operands is indeed a vector space
example_vec_space_04.operands[0].deduce_as_vec_space()
example_vec_space_04_assoc = example_vec_space_04.association(1, 2)
# This will not work, because we have a mixture of vectors and CartExps.
# The error message comes about because Prove-It is trying to treat R^3
# as a vector instead of a vector space
try:
example_vec_space_05_assoc = example_vec_space_05.association(1, 2)
except Exception as the_exception:
print("Exception: {}".format(the_exception))
TensorProd.disassociation()
for tensor products of sets¶Notice that TensorProd.disassociation()
will produce a complete disassociation if auto_simplify()
is allowed to proceed with no modifications, so here we first change the simplification direction 'ungroup' to False (and then reset back to True after this section).
TensorProd.change_simplification_directives(ungroup=False)
example_vec_space_01_assoc.rhs.disassociation(1)
example_vec_space_04_assoc.rhs.disassociation(1)
example_vec_space_04_assoc.rhs.disassociation(0)
example_vec_space_04_assoc.rhs.inner_expr().operands[0].disassociation(1)
TensorProd(TensorProd(x, TensorProd(y, z)), TensorProd(x, y)).disassociation(1)
# Reset to allow default auto_simplify() results
# (and notice what then happens in the next cell)
TensorProd.change_simplification_directives(ungroup=True)
# Now the disassociation() produces a complete disassociation
# despite our intention to only disassociate a piece
TensorProd(TensorProd(x, TensorProd(y, z)), TensorProd(x, y)).disassociation(1)
VecSum.factors_extraction()
¶# here the collection of scalars includes an index-dependent factor
scalar_mult_example_04.factors_extraction()
defaults.assumptions
# sometimes this works, and sometimes it doesn't
# how can it be non-deterministic? Is there something non-deterministic about the
# vector_spaces derivation?
# Here, all the scalar factors can be extracted:
display(scalar_mult_example_03)
scalar_mult_example_03.factors_extraction(assumptions = defaults.assumptions + scalar_mult_example_03.conditions.entries)
# In this case, one factor can be extracted, another cannot:
display(scalar_mult_example_04)
scalar_mult_example_04.factors_extraction()
# None of the factors are index-dependent
scalar_mult_example_02.factors_extraction()
# Just one vector of several is index-dependent
# while none of the scalars are index-dependent
display(scalar_mult_example_03)
scalar_mult_example_03.factors_extraction()
# If the scalar in ScalarMult is a single item?
display(scalar_mult_example_01)
scalar_mult_example_01.factors_extraction()
# If some scalars are index-dependent
# while none of the vectors themselves are?
display(scalar_mult_example_04)
scalar_mult_example_04.factors_extraction()
# multiple factors that can and cannot be extracted
display(scalar_mult_example_05)
scalar_mult_example_05.factors_extraction(field=None)
from proveit.numbers import Mult
add_01 = Add(Mult(alpha, beta), Mult(beta, gamma), Mult(alpha, Mult(gamma, beta)))
# not surprisingly, the Add.factorization() method doesn't work
# if the desired factor is buried too deeply in one of the expressions
display(add_01)
try:
add_01.factorization(beta)
except ValueError as the_exception:
print("ValueError: {}".format(the_exception))
add_02 = Add(Add(Mult(alpha, beta), beta), Mult(beta, gamma))
add_02.inner_expr().operands[0].factorization(beta)
VecAdd.factorization()
method¶vec_add_example_01 = VecAdd(ScalarMult(Mult(alpha, beta), x), ScalarMult(Mult(gamma, alpha), y))
defaults.assumptions
ScalarMult(alpha, ScalarMult(beta, x)).shallow_simplification()
vec_add_example_01
vec_add_example_01.factorization(alpha, pull='left', field=Complex)
TensorProd.remove_vec_on_both_sides_of_equals()
and TensorProd.insert_vec_on_both_sides_of_equals()
methods¶help(TensorProd.remove_vec_on_both_sides_of_equals)
help(TensorProd.insert_vec_on_both_sides_of_equals)
tp_01, tp_02 = (TensorProd(x, y, z), TensorProd(x, u, z))
equality_01 = Equals(tp_01, tp_02)
defaults.assumptions += (equality_01,)
We can access TensorProd.remove_vec_on_both_sides_of_equals()
and TensorProd.insert_vec_on_both_sides_of_equals()
via Equals.remove_vec_on_both_sides
and Equals.insert_vec_on_both_sides
respectively which are methods generated on-the-fly by finding methods with the _on_both_sides_of_equals
suffix associated with the Equals expression. The docstrings are set to be the same:
help(equality_01.remove_vec_on_both_sides)
equality_02 = equality_01.remove_vec_on_both_sides(0)
equality_03 = equality_02.remove_vec_on_both_sides(1)
The example above also demonstrates the auto-simplification of unary tensor products.
Now let's insert vectors into the tensor products. Starting with the special case where we don't start out with a tensor product, we must call the TensorProd.insert_vec_on_both_sides_of_equals
method directly:
equality_04 = TensorProd.insert_vec_on_both_sides_of_equals(
equality_03, 0, z)
In the furture, we could have CartExp
and other vector space expression classes implement left_tensor_prod_both_sides_of_equals
(and right_tensor_prod_both_sides_of_equals
) so then the above would be implemented via equality.left_tensor_prod_both_sides(z)
insead.
Now that we have tensor products on both sides, we can call insert_vec_on_both_sides
in the equality.
equality_05 = equality_04.insert_vec_on_both_sides(2, x)
ExprTuple.range_expansion()
method¶Operating on an ExprTuple whose single entry is an ExprRange that represents a finite list of elements, the ExprTuple.range_expansion()
method converts self to an ExprTuple with a finite listing of explicit arguments.
For example, letting $\text{expr_tuple_01} = (x_1,\ldots,x_3)$ and then calling expr_tuple_01.range_expansion()
deduces and returns
$\vdash ((x_1,\ldots,x_3) = (x_1, x_2, x_3))$
The reason for including this here in the demonstrations notebook for the linear_algebra package is that the method can be utilized to convert something like
$x_1 \otimes \ldots \otimes x_3$
to the more explicit
$x_1 \otimes x_2 \otimes x_3$
# create an example ExprRange
example_expr_range = ExprRange(i, IndexedVar(x, i), one, three)
# use the example ExprRange as the arg(s) for a TensorProd:
example_tensor_prod_over_range = TensorProd(example_expr_range)
# find the ExprTuple equivalent to the (ExprTuple-wrapped) ExprRange:
expr_range_eq_expr_tuple = ExprTuple(example_expr_range).range_expansion()
# find the equivalent explicit tensor prod
example_tensor_prod_over_range.inner_expr().operands.substitution(
expr_range_eq_expr_tuple)
# find the equivalent explicit tensor prod,
# and omit the wrapping inherited from the underlying theorem
example_tensor_prod_over_range.inner_expr().operands.substitution(
expr_range_eq_expr_tuple).with_wrapping_at()
%end demonstrations