Path: blob/master/sage/quadratic_forms/quadratic_form__variable_substitutions.py
4069 views
"""1Variable Substitution, Multiplication, Division, Scaling23"""4#*****************************************************************************5# Copyright (C) 2007 William Stein and Jonathan Hanke6#7# Distributed under the terms of the GNU General Public License (GPL)8#9# This code is distributed in the hope that it will be useful,10# but WITHOUT ANY WARRANTY; without even the implied warranty of11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12# General Public License for more details.13#14# The full text of the GPL is available at:15#16# http://www.gnu.org/licenses/17#*****************************************************************************1819import copy202122def swap_variables(self, r, s, in_place = False):23"""24Switch the variables `x_r` and `x_s` in the quadratic form25(replacing the original form if the in_place flag is True).2627INPUT:28`r`, `s` -- integers >= 02930OUTPUT:31a QuadraticForm (by default, otherwise none)3233EXAMPLES::3435sage: Q = QuadraticForm(ZZ, 4, range(1,11))36sage: Q37Quadratic form in 4 variables over Integer Ring with coefficients:38[ 1 2 3 4 ]39[ * 5 6 7 ]40[ * * 8 9 ]41[ * * * 10 ]424344sage: Q.swap_variables(0,2)45Quadratic form in 4 variables over Integer Ring with coefficients:46[ 8 6 3 9 ]47[ * 5 2 7 ]48[ * * 1 4 ]49[ * * * 10 ]505152sage: Q.swap_variables(0,2).swap_variables(0,2)53Quadratic form in 4 variables over Integer Ring with coefficients:54[ 1 2 3 4 ]55[ * 5 6 7 ]56[ * * 8 9 ]57[ * * * 10 ]5859"""60if (in_place == False):61Q = copy.deepcopy(self)62Q.__init__(self.base_ring(), self.dim(), self.coefficients())63Q.swap_variables(r,s,in_place=True)64return Q6566else:67## Switch diagonal elements68tmp = self[r,r]69self[r,r] = self[s,s]70self[s,s] = tmp7172## Switch off-diagonal elements73for i in range(self.dim()):74if (i != r) and (i != s):75tmp = self[r,i]76self[r,i] = self[s,i]77self[s,i] = tmp787980def multiply_variable(self, c, i, in_place = False):81"""82Replace the variables `x_i` by `c*x_i` in the quadratic form83(replacing the original form if the in_place flag is True).8485Here `c` must be an element of the base_ring defining the86quadratic form.8788INPUT:89`c` -- an element of Q.base_ring()9091`i` -- an integer >= 09293OUTPUT:94a QuadraticForm (by default, otherwise none)9596EXAMPLES::9798sage: Q = DiagonalQuadraticForm(ZZ, [1,9,5,7])99sage: Q.multiply_variable(5,0)100Quadratic form in 4 variables over Integer Ring with coefficients:101[ 25 0 0 0 ]102[ * 9 0 0 ]103[ * * 5 0 ]104[ * * * 7 ]105106"""107if (in_place == False):108Q = copy.deepcopy(self)109Q.__init__(self.base_ring(), self.dim(), self.coefficients())110Q.multiply_variable(c,i,in_place=True)111return Q112113else:114## Stretch the diagonal element115tmp = c * c * self[i,i]116self[i,i] = tmp117118## Switch off-diagonal elements119for k in range(self.dim()):120if (k != i):121tmp = c * self[k,i]122self[k,i] = tmp123124125def divide_variable(self, c, i, in_place = False):126"""127Replace the variables `x_i` by `(x_i)/c` in the quadratic form128(replacing the original form if the in_place flag is True).129130Here `c` must be an element of the base_ring defining the131quadratic form, and the division must be defined in the base132ring.133134INPUT:135`c` -- an element of Q.base_ring()136137`i` -- an integer >= 0138139OUTPUT:140a QuadraticForm (by default, otherwise none)141142EXAMPLES::143144sage: Q = DiagonalQuadraticForm(ZZ, [1,9,5,7])145sage: Q.divide_variable(3,1)146Quadratic form in 4 variables over Integer Ring with coefficients:147[ 1 0 0 0 ]148[ * 1 0 0 ]149[ * * 5 0 ]150[ * * * 7 ]151152"""153if (in_place == False):154Q = copy.deepcopy(self)155Q.__init__(self.base_ring(), self.dim(), self.coefficients())156Q.divide_variable(c,i,in_place=True)157return Q158159else:160## Stretch the diagonal element161tmp = self[i,i] / (c*c)162self[i,i] = tmp163164## Switch off-diagonal elements165for k in range(self.dim()):166if (k != i):167tmp = self[k,i] / c168self[k,i] = tmp169170171def scale_by_factor(self, c, change_value_ring_flag=False):172"""173Scale the values of the quadratic form by the number `c`, if174this is possible while still being defined over its base ring.175176If the flag is set to true, then this will alter the value ring177to be the field of fractions of the original ring (if necessary).178179INPUT:180`c` -- a scalar in the fraction field of the value ring of the form.181182OUTPUT:183A quadratic form of the same dimension184185EXAMPLES::186187sage: Q = DiagonalQuadraticForm(ZZ, [3,9,18,27])188sage: Q.scale_by_factor(3)189Quadratic form in 4 variables over Integer Ring with coefficients:190[ 9 0 0 0 ]191[ * 27 0 0 ]192[ * * 54 0 ]193[ * * * 81 ]194195sage: Q.scale_by_factor(1/3)196Quadratic form in 4 variables over Integer Ring with coefficients:197[ 1 0 0 0 ]198[ * 3 0 0 ]199[ * * 6 0 ]200[ * * * 9 ]201202"""203## Try to scale the coefficients while staying in the ring of values.204new_coeff_list = [x*c for x in self.coefficients()]205206## Check if we can preserve the value ring and return result. -- USE THE BASE_RING FOR NOW...207R = self.base_ring()208try:209list2 = [R(x) for x in new_coeff_list]210# This is a hack: we would like to use QuadraticForm here, but211# it doesn't work by scoping reasons.212Q = self.__class__(R, self.dim(), list2)213return Q214except:215if (change_value_ring_flag == False):216raise TypeError, "Oops! We could not rescale the lattice in this way and preserve its defining ring."217else:218raise UntestedCode, "This code is not tested by current doctests!"219F = R.fraction_field()220list2 = [F(x) for x in new_coeff_list]221Q = copy.deepcopy(self)222Q.__init__(self.dim(), F, list2, R) ## DEFINE THIS! IT WANTS TO SET THE EQUIVALENCE RING TO R, BUT WITH COEFFS IN F.223#Q.set_equivalence_ring(R)224return Q225226227def extract_variables(self, var_indices):228"""229Extract the variables (in order) whose indices are listed in230var_indices, to give a new quadratic form.231232INPUT:233var_indices -- a list of integers >= 0234235OUTPUT:236a QuadraticForm237238EXAMPLES::239240sage: Q = QuadraticForm(ZZ, 4, range(10)); Q241Quadratic form in 4 variables over Integer Ring with coefficients:242[ 0 1 2 3 ]243[ * 4 5 6 ]244[ * * 7 8 ]245[ * * * 9 ]246sage: Q.extract_variables([1,3])247Quadratic form in 2 variables over Integer Ring with coefficients:248[ 4 6 ]249[ * 9 ]250251"""252m = len(var_indices)253Q = copy.deepcopy(self)254Q.__init__(self.base_ring(), m)255for i in range(m):256for j in range(i, m):257Q[i,j] = self[ var_indices[i], var_indices[j] ]258259return Q260261262263def elementary_substitution(self, c, i, j, in_place = False): ## CHECK THIS!!!264"""265Perform the substitution `x_i --> x_i + c*x_j` (replacing the266original form if the in_place flag is True).267268INPUT:269`c` -- an element of Q.base_ring()270271`i`, `j` -- integers >= 0272273OUTPUT:274a QuadraticForm (by default, otherwise none)275276EXAMPLES::277278sage: Q = QuadraticForm(ZZ, 4, range(1,11))279sage: Q280Quadratic form in 4 variables over Integer Ring with coefficients:281[ 1 2 3 4 ]282[ * 5 6 7 ]283[ * * 8 9 ]284[ * * * 10 ]285286sage: Q.elementary_substitution(c=1, i=0, j=3)287Quadratic form in 4 variables over Integer Ring with coefficients:288[ 1 2 3 6 ]289[ * 5 6 9 ]290[ * * 8 12 ]291[ * * * 15 ]292293::294295sage: R = QuadraticForm(ZZ, 4, range(1,11))296sage: R297Quadratic form in 4 variables over Integer Ring with coefficients:298[ 1 2 3 4 ]299[ * 5 6 7 ]300[ * * 8 9 ]301[ * * * 10 ]302303::304305sage: M = Matrix(ZZ, 4, 4, [1,0,0,1,0,1,0,0,0,0,1,0,0,0,0,1])306sage: M307[1 0 0 1]308[0 1 0 0]309[0 0 1 0]310[0 0 0 1]311sage: R(M)312Quadratic form in 4 variables over Integer Ring with coefficients:313[ 1 2 3 6 ]314[ * 5 6 9 ]315[ * * 8 12 ]316[ * * * 15 ]317318"""319if (in_place == False):320Q = copy.deepcopy(self)321Q.__init__(self.base_ring(), self.dim(), self.coefficients())322Q.elementary_substitution(c, i, j, True)323return Q324325else:326## Adjust the a_{k,j} coefficients327ij_old = self[i,j] ## Store this since it's overwritten, but used in the a_{j,j} computation!328for k in range(self.dim()):329if (k != i) and (k != j):330ans = self[j,k] + c*self[i,k]331self.__setitem__((j,k), ans)332elif (k == j):333ans = self[j,k] + c*ij_old + c*c*self[i,i]334self[j,k] = ans335else:336ans = self[j,k] + 2*c*self[i,k]337self[j,k] = ans338339340341def add_symmetric(self, c, i, j, in_place = False):342"""343Performs the substitution `x_j --> x_j + c*x_i`, which has the344effect (on associated matrices) of symmetrically adding345`c * j`-th row/column to the `i`-th row/column.346347NOTE: This is meant for compatibility with previous code,348which implemented a matrix model for this class. It is used349in the local_normal_form() method.350351352INPUT:353`c` -- an element of Q.base_ring()354355`i`, `j` -- integers >= 0356357OUTPUT:358a QuadraticForm (by default, otherwise none)359360EXAMPLES::361362sage: Q = QuadraticForm(ZZ, 3, range(1,7)); Q363Quadratic form in 3 variables over Integer Ring with coefficients:364[ 1 2 3 ]365[ * 4 5 ]366[ * * 6 ]367sage: Q.add_symmetric(-1, 1, 0)368Quadratic form in 3 variables over Integer Ring with coefficients:369[ 1 0 3 ]370[ * 3 2 ]371[ * * 6 ]372sage: Q.add_symmetric(-3/2, 2, 0) ## ERROR: -3/2 isn't in the base ring ZZ373Traceback (most recent call last):374...375RuntimeError: Oops! This coefficient can't be coerced to an element of the base ring for the quadratic form.376377::378379sage: Q = QuadraticForm(QQ, 3, range(1,7)); Q380Quadratic form in 3 variables over Rational Field with coefficients:381[ 1 2 3 ]382[ * 4 5 ]383[ * * 6 ]384sage: Q.add_symmetric(-3/2, 2, 0)385Quadratic form in 3 variables over Rational Field with coefficients:386[ 1 2 0 ]387[ * 4 2 ]388[ * * 15/4 ]389390"""391return self.elementary_substitution(c, j, i, in_place)392393394395396397398