Path: blob/master/src/sage/geometry/linear_expression.py
8817 views
"""1Linear Expressions23A linear expression is just a linear polynomial in some (fixed)4variables. This class only implements linear expressions for others to5use.67EXAMPLES::89sage: from sage.geometry.linear_expression import LinearExpressionModule10sage: L.<x,y,z> = LinearExpressionModule(QQ); L11Module of linear expressions in variables x, y, z over Rational Field12sage: x + 2*y + 3*z + 413x + 2*y + 3*z + 41415You can also pass coefficients and a constant term to construct linear16expressions::1718sage: L([1, 2, 3], 4)19x + 2*y + 3*z + 420sage: L([(1, 2, 3), 4])21x + 2*y + 3*z + 422sage: L([4, 1, 2, 3]) # note: constant is first in single-tuple notation23x + 2*y + 3*z + 42425The linear expressions are a module under the base ring, so you can26add them and multiply them with scalars::2728sage: m = x + 2*y + 3*z + 429sage: 2*m302*x + 4*y + 6*z + 831sage: m+m322*x + 4*y + 6*z + 833sage: m-m340*x + 0*y + 0*z + 035"""3637from sage.structure.parent import Parent38from sage.structure.element import ModuleElement39from sage.structure.unique_representation import UniqueRepresentation40from sage.misc.cachefunc import cached_method41424344class LinearExpression(ModuleElement):45"""46A linear expression.4748A linear expression is just a linear polynomial in some (fixed)49variables.5051EXAMPLES::5253sage: from sage.geometry.linear_expression import LinearExpressionModule54sage: L.<x,y,z> = LinearExpressionModule(QQ)55sage: m = L([1, 2, 3], 4); m56x + 2*y + 3*z + 457sage: m2 = L([(1, 2, 3), 4]); m258x + 2*y + 3*z + 459sage: m3 = L([4, 1, 2, 3]); m3 # note: constant is first in single-tuple notation60x + 2*y + 3*z + 461sage: m == m262True63sage: m2 == m364True65sage: L.zero()660*x + 0*y + 0*z + 067sage: a = L([12, 2/3, -1], -2)68sage: a - m6911*x - 4/3*y - 4*z - 670sage: LZ.<x,y,z> = LinearExpressionModule(ZZ)71sage: a - LZ([2, -1, 3], 1)7210*x + 5/3*y - 4*z - 373"""74def __init__(self, parent, coefficients, constant, check=True):75"""76Initialize ``self``.7778TESTS::7980sage: from sage.geometry.linear_expression import LinearExpressionModule81sage: L.<x,y,z> = LinearExpressionModule(QQ)82sage: linear = L([1, 2, 3], 4) # indirect doctest83sage: linear.parent() is L84True8586sage: TestSuite(linear).run()87"""88super(LinearExpression, self).__init__(parent)89self._coeffs = coefficients90self._const = constant91if check:92if self._coeffs.parent() is not self.parent().ambient_module():93raise ValueError("cofficients are not in the ambient module")94if not self._coeffs.is_immutable():95raise ValueError("cofficients are not immutable")96if self._const.parent() is not self.parent().base_ring():97raise ValueError("the constant is not in the base ring")9899def A(self):100"""101Return the coefficient vector.102103OUTPUT:104105The coefficient vector of the linear expression.106107EXAMPLES::108109sage: from sage.geometry.linear_expression import LinearExpressionModule110sage: L.<x,y,z> = LinearExpressionModule(QQ)111sage: linear = L([1, 2, 3], 4); linear112x + 2*y + 3*z + 4113sage: linear.A()114(1, 2, 3)115sage: linear.b()1164117"""118return self._coeffs119120def b(self):121"""122Return the constant term.123124OUTPUT:125126The constant term of the linear expression.127128EXAMPLES::129130sage: from sage.geometry.linear_expression import LinearExpressionModule131sage: L.<x,y,z> = LinearExpressionModule(QQ)132sage: linear = L([1, 2, 3], 4); linear133x + 2*y + 3*z + 4134sage: linear.A()135(1, 2, 3)136sage: linear.b()1374138"""139return self._const140141constant_term = b142143def coefficients(self):144"""145Return all coefficients.146147OUTPUT:148149The constant (as first entry) and coefficients of the linear150terms (as subsequent entries) in a list.151152EXAMPLES::153154sage: from sage.geometry.linear_expression import LinearExpressionModule155sage: L.<x,y,z> = LinearExpressionModule(QQ)156sage: linear = L([1, 2, 3], 4); linear157x + 2*y + 3*z + 4158sage: linear.coefficients()159[4, 1, 2, 3]160"""161return [self._const] + list(self._coeffs)162163def _repr_vector(self, variable='x'):164"""165Return a string representation.166167INPUT:168169- ``variable`` -- string; the name of the variable vector170171EXAMPLES::172173sage: from sage.geometry.linear_expression import LinearExpressionModule174sage: L.<x,y,z> = LinearExpressionModule(QQ)175sage: L([1, 2, 3], 4)._repr_vector()176'(1, 2, 3) x + 4 = 0'177sage: L([-1, -2, -3], -4)._repr_vector('u')178'(-1, -2, -3) u - 4 = 0'179"""180atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic')181constant = repr(self._const)182if not atomic_repr:183constant = '({0})'.format(constant)184constant = '+ {0}'.format(constant).replace('+ -', '- ')185return '{0} {1} {2} = 0'.format(repr(self._coeffs), variable, constant)186187def _repr_linear(self, include_zero=True, include_constant=True, multiplication='*'):188"""189Return a representation as a linear polynomial.190191INPUT:192193- ``include_zero`` -- whether to include terms with zero194coefficient195196- ``include_constant`` -- whether to include the constant197term198199- ``multiplication`` -- string (optional, default: ``*``); the200multiplication symbol to use201202OUTPUT:203204A string.205206EXAMPLES::207208sage: from sage.geometry.linear_expression import LinearExpressionModule209sage: L.<x,y,z> = LinearExpressionModule(QQ)210sage: L([1, 2, 3], 4)._repr_linear()211'x + 2*y + 3*z + 4'212sage: L([-1, -2, -3], -4)._repr_linear()213'-x - 2*y - 3*z - 4'214sage: L([0, 0, 0], 1)._repr_linear()215'0*x + 0*y + 0*z + 1'216sage: L([0, 0, 0], 0)._repr_linear()217'0*x + 0*y + 0*z + 0'218219sage: R.<u,v> = QQ[]220sage: L.<x,y,z> = LinearExpressionModule(R)221sage: L([-u+v+1, -3*u-2, 3], -4*u+v)._repr_linear()222'(-u + v + 1)*x + (-3*u - 2)*y + 3*z - 4*u + v'223224sage: L.<x,y,z> = LinearExpressionModule(QQ)225sage: L([1, 0, 3], 0)._repr_linear()226'x + 0*y + 3*z + 0'227sage: L([1, 0, 3], 0)._repr_linear(include_zero=False)228'x + 3*z'229sage: L([1, 0, 3], 1)._repr_linear(include_constant=False, multiplication='.')230'x + 0.y + 3.z'231sage: L([1, 0, 3], 1)._repr_linear(include_zero=False, include_constant=False)232'x + 3*z'233sage: L([0, 0, 0], 0)._repr_linear(include_zero=False)234'0'235"""236atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic')237names = [multiplication+n for n in self.parent()._names]238terms = zip(self._coeffs, names)239if include_constant:240terms += [(self._const, '')]241if not include_zero:242terms = [t for t in terms if t[0] != 0]243if len(terms) == 0:244return '0'245summands = []246for coeff, name in terms:247coeff = str(coeff)248if not atomic_repr and name != '' and any(c in coeff for c in ['+', '-']):249coeff = '({0})'.format(coeff)250summands.append(coeff+name)251s = ' ' + ' + '.join(summands)252s = s.replace(' + -', ' - ')253s = s.replace(' 1'+multiplication, ' ')254s = s.replace(' -1'+multiplication, ' -')255return s[1:]256257_repr_ = _repr_linear258259def _add_(self, other):260"""261Add two linear expressions.262263EXAMPLES::264265sage: from sage.geometry.linear_expression import LinearExpressionModule266sage: L.<x,y,z> = LinearExpressionModule(QQ)267sage: a = L([1, 2, 3], 4)268sage: b = L([-1, 3, -3], 0)269sage: a + b2700*x + 5*y + 0*z + 4271sage: a - b2722*x - y + 6*z + 4273"""274const = self._const + other._const275coeffs = self._coeffs + other._coeffs276coeffs.set_immutable()277return self.__class__(self.parent(), coeffs, const)278279def _rmul_(self, scalar):280"""281Multiply a linear expression by a scalar.282283EXAMPLES::284285sage: from sage.geometry.linear_expression import LinearExpressionModule286sage: L.<x,y,z> = LinearExpressionModule(QQ)287sage: a = L([1, 2, 3], 4); a288x + 2*y + 3*z + 4289sage: 2 * a2902*x + 4*y + 6*z + 8291sage: a * 22922*x + 4*y + 6*z + 8293sage: -a294-x - 2*y - 3*z - 4295sage: RDF(1) * a2961.0*x + 2.0*y + 3.0*z + 4.0297298TESTS::299300sage: a._lmul_(2)3012*x + 4*y + 6*z + 8302"""303const = scalar * self._const304coeffs = scalar * self._coeffs305coeffs.set_immutable()306return self.__class__(self.parent(), coeffs, const)307308_lmul_ = _rmul_309310def _acted_upon_(self, scalar, self_on_left):311"""312Action by scalars that do not live in the base ring.313314EXAMPLES::315316sage: from sage.geometry.linear_expression import LinearExpressionModule317sage: L.<x,y,z> = LinearExpressionModule(QQ)318sage: a = x + 2*y + 3*z + 4319sage: a * RDF(3)3203.0*x + 6.0*y + 9.0*z + 12.0321"""322base_ring = scalar.base_ring()323parent = self.parent().change_ring(base_ring)324changed = parent(self)325return changed._rmul_(scalar)326327def change_ring(self, base_ring):328"""329Change the base ring of this linear expression.330331INPUT:332333- ``base_ring`` -- a ring; the new base ring334335OUTPUT:336337A new linear expression over the new base ring.338339EXAMPLES::340341sage: from sage.geometry.linear_expression import LinearExpressionModule342sage: L.<x,y,z> = LinearExpressionModule(QQ)343sage: a = x + 2*y + 3*z + 4; a344x + 2*y + 3*z + 4345sage: a.change_ring(RDF)3461.0*x + 2.0*y + 3.0*z + 4.0347"""348P = self.parent()349if P.base_ring() is base_ring:350return self351return P.change_ring(base_ring)(self)352353def __cmp__(self, other):354"""355Compare two linear expressions.356357INPUT:358359- ``other`` -- another linear expression (will be enforced by360the coercion framework)361362EXAMPLES::363364sage: from sage.geometry.linear_expression import LinearExpressionModule365sage: L.<x> = LinearExpressionModule(QQ)366sage: x == L([0, 1])367True368sage: x == x + 1369False370371sage: M.<x> = LinearExpressionModule(ZZ)372sage: L.gen(0) == M.gen(0) # because there is a conversion373True374sage: L.gen(0) == L(M.gen(0)) # this is the conversion375True376377sage: x == 'test'378False379"""380assert (type(self) is type(other)) and (self.parent() is other.parent()) # guaranteed by framework381c = cmp(self._coeffs, other._coeffs)382if c != 0: return c383c = cmp(self._const, other._const)384return c385386def evaluate(self, point):387"""388Evaluate the linear expression.389390INPUT:391392- ``point`` -- list/tuple/iterable of coordinates; the393coordinates of a point394395OUTPUT:396397The linear expression `Ax + b` evaluated at the point `x`.398399EXAMPLES::400401sage: from sage.geometry.linear_expression import LinearExpressionModule402sage: L.<x,y> = LinearExpressionModule(QQ)403sage: ex = 2*x + 3* y + 4404sage: ex.evaluate([1,1])4059406sage: ex([1,1]) # syntactic sugar4079408sage: ex([pi, e])4092*pi + 3*e + 4410"""411try:412point = self.parent().ambient_module()(point)413except TypeError:414from sage.matrix.constructor import vector415point = vector(point)416return self._coeffs * point + self._const417418__call__ = evaluate419420421422423class LinearExpressionModule(Parent, UniqueRepresentation):424"""425The module of linear expressions.426427This is the module of linear polynomials which is the parent for428linear expressions.429430EXAMPLES::431432sage: from sage.geometry.linear_expression import LinearExpressionModule433sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))434sage: L435Module of linear expressions in variables x, y, z over Rational Field436sage: L.an_element()437x + 0*y + 0*z + 0438"""439Element = LinearExpression440441def __init__(self, base_ring, names=tuple()):442"""443Initialize ``self``.444445TESTS::446447sage: from sage.geometry.linear_expression import LinearExpressionModule448sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))449sage: type(L)450<class 'sage.geometry.linear_expression.LinearExpressionModule_with_category'>451sage: L.base_ring()452Rational Field453454sage: TestSuite(L).run()455456sage: L = LinearExpressionModule(QQ)457sage: TestSuite(L).run()458"""459from sage.categories.modules import Modules460super(LinearExpressionModule, self).__init__(base_ring, category=Modules(base_ring))461self._names = names462463@cached_method464def ngens(self):465"""466Return the number of linear variables.467468OUTPUT:469470An integer.471472EXAMPLES::473474sage: from sage.geometry.linear_expression import LinearExpressionModule475sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))476sage: L.ngens()4773478"""479return len(self._names)480481@cached_method482def gens(self):483"""484Return the generators.485486OUTPUT:487488A tuple of linear expressions, one for each linear variable.489490EXAMPLES::491492sage: from sage.geometry.linear_expression import LinearExpressionModule493sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))494sage: L.gens()495(x + 0*y + 0*z + 0, 0*x + y + 0*z + 0, 0*x + 0*y + z + 0)496"""497from sage.matrix.constructor import identity_matrix498identity = identity_matrix(self.base_ring(), self.ngens())499return tuple(self(e, 0) for e in identity.rows())500501def gen(self, i):502"""503Return the `i`-th generator.504505INPUT:506507- ``i`` -- integer508509OUTPUT:510511A linear expression.512513EXAMPLES::514515sage: from sage.geometry.linear_expression import LinearExpressionModule516sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))517sage: L.gen(0)518x + 0*y + 0*z + 0519"""520return self.gens()[i]521522def _element_constructor_(self, arg0, arg1=None):523"""524The element constructor.525526This is part of the Sage parent/element framework.527528TESTS::529530sage: from sage.geometry.linear_expression import LinearExpressionModule531sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))532533Construct from coeffients and constant term::534535sage: L._element_constructor([1, 2, 3], 4)536x + 2*y + 3*z + 4537sage: L._element_constructor(vector(ZZ, [1, 2, 3]), 4)538x + 2*y + 3*z + 4539540Construct constant linear expression term::541542sage: L._element_constructor(4)5430*x + 0*y + 0*z + 4544545Construct from list/tuple/iterable::546547sage: L._element_constructor(vector([4, 1, 2, 3]))548x + 2*y + 3*z + 4549550Construct from a pair ``(coefficients, constant)``::551552sage: L([(1, 2, 3), 4])553x + 2*y + 3*z + 4554555Construct from linear expression::556557sage: M = LinearExpressionModule(ZZ, ('u', 'v', 'w'))558sage: m = M([1, 2, 3], 4)559sage: L._element_constructor(m)560x + 2*y + 3*z + 4561"""562R = self.base_ring()563if arg1 is None:564if arg0 in R:565const = arg0566coeffs = self.ambient_module().zero()567elif isinstance(arg0, LinearExpression):568# Construct from linear expression569const = arg0.b()570coeffs = arg0.A()571elif isinstance(arg0, (list, tuple)) and len(arg0) == 2 and isinstance(arg0[0], (list, tuple)):572# Construct from pair573coeffs = arg0[0]574const = arg0[1]575else:576# Construct from list/tuple/iterable::577try:578arg0 = arg0.coefficients()579except AttributeError:580arg0 = list(arg0)581const = arg0[0]582coeffs = arg0[1:]583else:584# arg1 is not None, construct from coeffients and constant term::585coeffs = list(arg0)586const = arg1587coeffs = self.ambient_module()(coeffs)588coeffs.set_immutable()589const = R(const)590return self.element_class(self, coeffs, const)591592def random_element(self):593"""594Return a random element.595596EXAMPLES::597598sage: from sage.geometry.linear_expression import LinearExpressionModule599sage: L.<x,y,z> = LinearExpressionModule(QQ)600sage: L.random_element()601-1/2*x - 1/95*y + 1/2*z - 12602"""603A = self.ambient_module().random_element()604b = self.base_ring().random_element()605return self(A, b)606607@cached_method608def ambient_module(self):609"""610Return the ambient module.611612.. SEEALSO::613614:meth:`ambient_vector_space`615616OUTPUT:617618The domain of the linear expressions as a free module over the619base ring.620621EXAMPLES::622623sage: from sage.geometry.linear_expression import LinearExpressionModule624sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))625sage: L.ambient_module()626Vector space of dimension 3 over Rational Field627sage: M = LinearExpressionModule(ZZ, ('r', 's'))628sage: M.ambient_module()629Ambient free module of rank 2 over the principal ideal domain Integer Ring630sage: M.ambient_vector_space()631Vector space of dimension 2 over Rational Field632"""633from sage.modules.all import FreeModule634return FreeModule(self.base_ring(), self.ngens())635636@cached_method637def ambient_vector_space(self):638"""639Return the ambient vector space.640641.. SEEALSO::642643:meth:`ambient_module`644645OUTPUT:646647The vector space (over the fraction field of the base ring)648where the linear expressions live.649650EXAMPLES::651652sage: from sage.geometry.linear_expression import LinearExpressionModule653sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))654sage: L.ambient_vector_space()655Vector space of dimension 3 over Rational Field656sage: M = LinearExpressionModule(ZZ, ('r', 's'))657sage: M.ambient_module()658Ambient free module of rank 2 over the principal ideal domain Integer Ring659sage: M.ambient_vector_space()660Vector space of dimension 2 over Rational Field661"""662from sage.modules.all import VectorSpace663field = self.base_ring().fraction_field()664return VectorSpace(field, self.ngens())665666def _coerce_map_from_(self, P):667"""668Return whether there is a coercion.669670TESTS::671672sage: from sage.geometry.linear_expression import LinearExpressionModule673sage: L.<x> = LinearExpressionModule(QQ)674sage: M.<y> = LinearExpressionModule(ZZ)675sage: L.coerce_map_from(M)676Conversion map:677From: Module of linear expressions in variable y over Integer Ring678To: Module of linear expressions in variable x over Rational Field679sage: M.coerce_map_from(L)680681sage: M.coerce_map_from(ZZ)682Conversion map:683From: Integer Ring684To: Module of linear expressions in variable y over Integer Ring685sage: M.coerce_map_from(QQ)686"""687if self.base().has_coerce_map_from(P):688return True689try:690return self.ngens() == P.ngens() and \691self.base().has_coerce_map_from(P.base())692except AttributeError:693pass694return super(LinearExpressionModule, self)._coerce_map_from_(P)695696def _repr_(self):697"""698Return a string representation.699700OUTPUT:701702A string.703704EXAMPLES::705706sage: from sage.geometry.linear_expression import LinearExpressionModule707sage: L.<x> = LinearExpressionModule(QQ); L708Module of linear expressions in variable x over Rational Field709"""710return 'Module of linear expressions in variable{2} {0} over {1}'.format(711', '.join(self._names), self.base_ring(), 's' if self.ngens() > 1 else '')712713def change_ring(self, base_ring):714"""715Return a new module with a changed base ring.716717INPUT:718719- ``base_ring`` -- a ring; the new base ring720721OUTPUT:722723A new linear expression over the new base ring.724725EXAMPLES::726727sage: from sage.geometry.linear_expression import LinearExpressionModule728sage: M.<y> = LinearExpressionModule(ZZ)729sage: L = M.change_ring(QQ); L730Module of linear expressions in variable y over Rational Field731732TESTS::733734sage: L.change_ring(QQ) is L735True736"""737return LinearExpressionModule(base_ring, self._names)738739740741