| 1 | ############################################################# |
|---|
| 2 | # |
|---|
| 3 | # Sparse Vector over mpq_t (the GMP rationals) |
|---|
| 4 | # |
|---|
| 5 | ############################################################# |
|---|
| 6 | |
|---|
| 7 | include "../ext/cdefs.pxi" |
|---|
| 8 | |
|---|
| 9 | cdef struct mpq_vector: |
|---|
| 10 | mpq_t *entries # array of nonzero entries |
|---|
| 11 | int *positions # positions of those nonzero entries, starting at 0 |
|---|
| 12 | int degree # the degree of this sparse vector |
|---|
| 13 | int num_nonzero # the number of nonzero entries of this vector. |
|---|
| 14 | |
|---|
| 15 | cdef int allocate_mpq_vector(mpq_vector* v, int num_nonzero) except -1: |
|---|
| 16 | """ |
|---|
| 17 | Allocate memory for a mpq_vector -- most user code won't call this. |
|---|
| 18 | num_nonzero is the number of nonzero entries to allocate memory for. |
|---|
| 19 | |
|---|
| 20 | This function *does* call mpq_init on each mpq_t that is allocated. |
|---|
| 21 | It does *not* clear the entries of v, if there are any. |
|---|
| 22 | """ |
|---|
| 23 | cdef int i |
|---|
| 24 | v.entries = <mpq_t*>sage_malloc(num_nonzero*sizeof(mpq_t)) |
|---|
| 25 | if v.entries == <mpq_t*> 0: |
|---|
| 26 | raise MemoryError, "Error allocating memory" |
|---|
| 27 | for i from 0 <= i < num_nonzero: |
|---|
| 28 | mpq_init(v.entries[i]) |
|---|
| 29 | v.positions = <int*>sage_malloc(num_nonzero*sizeof(int)) |
|---|
| 30 | if v.positions == <int*> 0: |
|---|
| 31 | raise MemoryError, "Error allocating memory" |
|---|
| 32 | return 0 |
|---|
| 33 | |
|---|
| 34 | cdef int init_mpq_vector(mpq_vector* v, int degree, int num_nonzero) except -1: |
|---|
| 35 | """ |
|---|
| 36 | Initialize a mpq_vector -- most user code *will* call this. |
|---|
| 37 | """ |
|---|
| 38 | allocate_mpq_vector(v, num_nonzero) |
|---|
| 39 | v.num_nonzero = num_nonzero |
|---|
| 40 | v.degree = degree |
|---|
| 41 | |
|---|
| 42 | cdef void clear_mpq_vector(mpq_vector* v): |
|---|
| 43 | cdef int i |
|---|
| 44 | # Free all mpq objects allocated in creating v |
|---|
| 45 | for i from 0 <= i < v.num_nonzero: |
|---|
| 46 | mpq_clear(v.entries[i]) |
|---|
| 47 | # Free entries and positions of those entries. |
|---|
| 48 | # These were allocated from the Python heap. |
|---|
| 49 | # If init_mpq_vector was not called, then this |
|---|
| 50 | # will (of course!) cause a core dump. |
|---|
| 51 | sage_free(v.entries) |
|---|
| 52 | sage_free(v.positions) |
|---|
| 53 | |
|---|
| 54 | cdef int mpq_binary_search0(mpq_t* v, int n, mpq_t x): |
|---|
| 55 | """ |
|---|
| 56 | Find the position of the rational x in the array v, which has length n. |
|---|
| 57 | Returns -1 if x is not in the array v. |
|---|
| 58 | """ |
|---|
| 59 | cdef int i, j, k, c |
|---|
| 60 | if n == 0: |
|---|
| 61 | return -1 |
|---|
| 62 | i = 0 |
|---|
| 63 | j = n-1 |
|---|
| 64 | while i<=j: |
|---|
| 65 | if i == j: |
|---|
| 66 | if mpq_equal(v[i],x): |
|---|
| 67 | return i |
|---|
| 68 | return -1 |
|---|
| 69 | k = (i+j)/2 |
|---|
| 70 | c = mpq_cmp(v[k],x) |
|---|
| 71 | if c > 0: # v[k] > x |
|---|
| 72 | j = k-1 |
|---|
| 73 | elif c < 0: # v[k] < x |
|---|
| 74 | i = k+1 |
|---|
| 75 | else: # only possibility is that v[k] == x |
|---|
| 76 | return k |
|---|
| 77 | return -1 |
|---|
| 78 | |
|---|
| 79 | cdef int mpq_binary_search(mpq_t* v, int n, mpq_t x, int* ins): |
|---|
| 80 | """ |
|---|
| 81 | Find the position of the integer x in the array v, which has length n. |
|---|
| 82 | Returns -1 if x is not in the array v, and in this case ins is |
|---|
| 83 | set equal to the position where x should be inserted in order to |
|---|
| 84 | obtain an ordered array. |
|---|
| 85 | |
|---|
| 86 | INPUT: |
|---|
| 87 | v -- array of mpq_t (rational) |
|---|
| 88 | n -- integer (length of array v) |
|---|
| 89 | x -- mpq_t (rational) |
|---|
| 90 | OUTPUT: |
|---|
| 91 | position of x (as an int) |
|---|
| 92 | ins -- (call be pointer), the insertion point if x is not found. |
|---|
| 93 | """ |
|---|
| 94 | cdef int i, j, k, c |
|---|
| 95 | if n == 0: |
|---|
| 96 | ins[0] = 0 |
|---|
| 97 | return -1 |
|---|
| 98 | i = 0 |
|---|
| 99 | j = n-1 |
|---|
| 100 | while i<=j: |
|---|
| 101 | if i == j: |
|---|
| 102 | c = mpq_cmp(v[i],x) |
|---|
| 103 | if c == 0: # v[i] == x |
|---|
| 104 | ins[0] = i |
|---|
| 105 | return i |
|---|
| 106 | if c < 0: # v[i] < x |
|---|
| 107 | ins[0] = i + 1 |
|---|
| 108 | else: |
|---|
| 109 | ins[0] = i |
|---|
| 110 | return -1 |
|---|
| 111 | k = (i+j)/2 |
|---|
| 112 | c = mpq_cmp(v[k], x) |
|---|
| 113 | if c > 0: # v[k] > x |
|---|
| 114 | j = k-1 |
|---|
| 115 | elif c < 0: # v[k] < x |
|---|
| 116 | i = k+1 |
|---|
| 117 | else: # only possibility is that v[k] == x |
|---|
| 118 | ins[0] = k |
|---|
| 119 | return k |
|---|
| 120 | # end while |
|---|
| 121 | ins[0] = j+1 |
|---|
| 122 | return -1 |
|---|
| 123 | |
|---|
| 124 | cdef int mpq_vector_get_entry(mpq_t* ans, mpq_vector* v, int n) except -1: |
|---|
| 125 | """ |
|---|
| 126 | Returns the n-th entry of the sparse vector v. This |
|---|
| 127 | would be v[n] in Python syntax. |
|---|
| 128 | |
|---|
| 129 | The return is done using the pointer ans, which is to an mpq_t |
|---|
| 130 | that *must* have been initialized using mpq_init. |
|---|
| 131 | """ |
|---|
| 132 | if n >= v.degree: |
|---|
| 133 | raise IndexError, "Index must be between 0 and %s."%(v.degree - 1) |
|---|
| 134 | cdef int m |
|---|
| 135 | m = binary_search0(v.positions, v.num_nonzero, n) |
|---|
| 136 | if m == -1: |
|---|
| 137 | mpq_set_si(ans[0], 0,1) |
|---|
| 138 | return 0 |
|---|
| 139 | mpq_set(ans[0], v.entries[m]) |
|---|
| 140 | return 0 |
|---|
| 141 | |
|---|
| 142 | cdef object mpq_vector_to_list(mpq_vector* v): |
|---|
| 143 | """ |
|---|
| 144 | Returns a Python list of 2-tuples (i,x), where x=v[i] runs |
|---|
| 145 | through the nonzero elements of x, in order. |
|---|
| 146 | """ |
|---|
| 147 | cdef X |
|---|
| 148 | cdef sage.rings.rational.Rational a |
|---|
| 149 | cdef int i |
|---|
| 150 | X = [] |
|---|
| 151 | for i from 0 <= i < v.num_nonzero: |
|---|
| 152 | a = sage.rings.rational.Rational() |
|---|
| 153 | a.set_from_mpq(v.entries[i]) |
|---|
| 154 | X.append( (v.positions[i], a) ) |
|---|
| 155 | return X |
|---|
| 156 | |
|---|
| 157 | |
|---|
| 158 | cdef int mpq_vector_set_entry(mpq_vector* v, int n, mpq_t x) except -1: |
|---|
| 159 | """ |
|---|
| 160 | Set the n-th component of the sparse vector v equal to x. |
|---|
| 161 | This would be v[n] = x in Python syntax. |
|---|
| 162 | """ |
|---|
| 163 | if n >= v.degree or n < 0: |
|---|
| 164 | raise IndexError, "Index must be between 0 and the degree minus 1." |
|---|
| 165 | return -1 |
|---|
| 166 | cdef int i, m, ins |
|---|
| 167 | cdef int m2, ins2 |
|---|
| 168 | cdef int *pos |
|---|
| 169 | cdef mpq_t *e |
|---|
| 170 | |
|---|
| 171 | m = binary_search(v.positions, v.num_nonzero, n, &ins) |
|---|
| 172 | |
|---|
| 173 | if m != -1: |
|---|
| 174 | # The position n was found in the array of positions. |
|---|
| 175 | # Now there are two cases: |
|---|
| 176 | # 1. x =/= 0, which is easy, and |
|---|
| 177 | # 2. x = 0, in which case we have to recopy |
|---|
| 178 | # positions and entries, without the m-th |
|---|
| 179 | # element, and change num_nonzero. |
|---|
| 180 | if mpq_sgn(x) != 0: # x != 0, case 1 |
|---|
| 181 | # v.entries[m] = x |
|---|
| 182 | mpq_set(v.entries[m], x) |
|---|
| 183 | else: # case 2 |
|---|
| 184 | e = v.entries |
|---|
| 185 | pos = v.positions |
|---|
| 186 | allocate_mpq_vector(v, v.num_nonzero - 1) |
|---|
| 187 | for i from 0 <= i < m: |
|---|
| 188 | # v.entries[i] = e[i] |
|---|
| 189 | mpq_set(v.entries[i], e[i]) |
|---|
| 190 | v.positions[i] = pos[i] |
|---|
| 191 | for i from m < i < v.num_nonzero: |
|---|
| 192 | # v.entries[i-1] = e[i] |
|---|
| 193 | mpq_set(v.entries[i-1], e[i]) |
|---|
| 194 | v.positions[i-1] = pos[i] |
|---|
| 195 | sage_free(e) |
|---|
| 196 | sage_free(pos) |
|---|
| 197 | v.num_nonzero = v.num_nonzero - 1 |
|---|
| 198 | else: |
|---|
| 199 | # Allocate new memory and copy over elements from the |
|---|
| 200 | # old array. This is similar to case 2 above, |
|---|
| 201 | # except we are inserting a new entry rather than |
|---|
| 202 | # deleting an old one. The new entry should be inserted |
|---|
| 203 | # at position ins, which was computed using binary search. |
|---|
| 204 | # |
|---|
| 205 | # There is one exception -- if the new entry is 0, we |
|---|
| 206 | # do nothing and return. |
|---|
| 207 | if mpq_sgn(x) == 0: |
|---|
| 208 | return 0 |
|---|
| 209 | v.num_nonzero = v.num_nonzero + 1 |
|---|
| 210 | e = v.entries |
|---|
| 211 | pos = v.positions |
|---|
| 212 | allocate_mpq_vector(v, v.num_nonzero) |
|---|
| 213 | for i from 0 <= i < ins: |
|---|
| 214 | # v.entries[i] = e[i] |
|---|
| 215 | mpq_set(v.entries[i], e[i]) |
|---|
| 216 | v.positions[i] = pos[i] |
|---|
| 217 | # v.entries[ins] = x |
|---|
| 218 | mpq_set(v.entries[ins], x) |
|---|
| 219 | v.positions[ins] = n |
|---|
| 220 | for i from ins < i < v.num_nonzero: |
|---|
| 221 | mpq_set(v.entries[i], e[i-1]) |
|---|
| 222 | v.positions[i] = pos[i-1] |
|---|
| 223 | # Free the memory occupie by GMP rationals. |
|---|
| 224 | # This -1 is because we incremented v.num_nonzero above. |
|---|
| 225 | for i from 0 <= i < v.num_nonzero-1: |
|---|
| 226 | mpq_clear(e[i]) |
|---|
| 227 | sage_free(e) |
|---|
| 228 | sage_free(pos) |
|---|
| 229 | |
|---|
| 230 | |
|---|
| 231 | |
|---|
| 232 | cdef mpq_t mpq_set_tmp |
|---|
| 233 | mpq_init(mpq_set_tmp) |
|---|
| 234 | cdef int mpq_vector_set_entry_str(mpq_vector* v, int n, char *x_str) except -1: |
|---|
| 235 | """ |
|---|
| 236 | Set the n-th component of the sparse vector v equal to x. |
|---|
| 237 | This would be v[n] = x in Python syntax. |
|---|
| 238 | """ |
|---|
| 239 | mpq_set_str(mpq_set_tmp, x_str, 0) |
|---|
| 240 | mpq_vector_set_entry(v, n, mpq_set_tmp) |
|---|
| 241 | |
|---|
| 242 | |
|---|
| 243 | cdef int add_mpq_vector_init(mpq_vector* sum, |
|---|
| 244 | mpq_vector* v, |
|---|
| 245 | mpq_vector* w, |
|---|
| 246 | mpq_t multiple) except -1: |
|---|
| 247 | """ |
|---|
| 248 | Initialize sum and set sum = v + multiple*w. |
|---|
| 249 | """ |
|---|
| 250 | if v.degree != w.degree: |
|---|
| 251 | print "Can't add vectors of degree %s and %s"%(v.degree, w.degree) |
|---|
| 252 | raise ArithmeticError, "The vectors must have the same degree." |
|---|
| 253 | |
|---|
| 254 | cdef int nz, i, j, k, do_multiply |
|---|
| 255 | cdef mpq_vector* z |
|---|
| 256 | cdef mpq_t tmp |
|---|
| 257 | mpq_init(tmp) |
|---|
| 258 | if mpq_cmp_si(multiple, 0, 1) == 0: |
|---|
| 259 | init_mpq_vector(sum, v.degree, 0) |
|---|
| 260 | return 0 |
|---|
| 261 | # Do not do the multiply if the multiple is 1. |
|---|
| 262 | do_multiply = mpq_cmp_si(multiple, 1,1) |
|---|
| 263 | |
|---|
| 264 | z = sum |
|---|
| 265 | # ALGORITHM: |
|---|
| 266 | # 1. Allocate enough memory to hold the union of the two |
|---|
| 267 | # lists of positions. We allocate the sum of the number |
|---|
| 268 | # of positions of both (up to the degree), to avoid |
|---|
| 269 | # having to make two passes. This might be slightly wasteful of |
|---|
| 270 | # memory, but is faster. |
|---|
| 271 | # 2. Move along the entries of v and w, copying them into the |
|---|
| 272 | # new position / entry array. When position are the same, |
|---|
| 273 | # add modulo p. |
|---|
| 274 | # 3. Set num_nonzero and return success code. |
|---|
| 275 | |
|---|
| 276 | # 1. Allocate memory: |
|---|
| 277 | nz = v.num_nonzero + w.num_nonzero |
|---|
| 278 | if nz > v.degree: nz = v.degree |
|---|
| 279 | init_mpq_vector(z, v.degree, nz) |
|---|
| 280 | # 2. Merge entries |
|---|
| 281 | i = 0 # index into entries of v |
|---|
| 282 | j = 0 # index into entries of w |
|---|
| 283 | k = 0 # index into z (the vector we are creating) |
|---|
| 284 | while i < v.num_nonzero or j < w.num_nonzero: |
|---|
| 285 | if i >= v.num_nonzero: # just copy w in |
|---|
| 286 | z.positions[k] = w.positions[j] |
|---|
| 287 | |
|---|
| 288 | if do_multiply: |
|---|
| 289 | # This means: z.entries[k] = (multiple*w.entries[j]) |
|---|
| 290 | mpq_mul(z.entries[k], multiple, w.entries[j]) |
|---|
| 291 | else: |
|---|
| 292 | mpq_set(z.entries[k], w.entries[j]) |
|---|
| 293 | j = j + 1 |
|---|
| 294 | k = k + 1 |
|---|
| 295 | elif j >= w.num_nonzero: # just copy v in |
|---|
| 296 | z.positions[k] = v.positions[i] |
|---|
| 297 | # This means: z.entries[k] = v.entries[i] |
|---|
| 298 | mpq_set(z.entries[k], v.entries[i]) |
|---|
| 299 | i = i + 1 |
|---|
| 300 | k = k + 1 |
|---|
| 301 | elif v.positions[i] < w.positions[j]: # copy entry from v in |
|---|
| 302 | z.positions[k] = v.positions[i] |
|---|
| 303 | # This means: z.entries[k] = v.entries[i] |
|---|
| 304 | mpq_set(z.entries[k], v.entries[i]) |
|---|
| 305 | i = i + 1 |
|---|
| 306 | k = k + 1 |
|---|
| 307 | elif v.positions[i] > w.positions[j]: # copy entry from w in |
|---|
| 308 | if do_multiply: |
|---|
| 309 | # This means: tmp = multiple*w.entries[j] |
|---|
| 310 | mpq_mul(tmp, multiple, w.entries[j]) |
|---|
| 311 | # This means: z.entries[k] = tmp |
|---|
| 312 | mpq_set(z.entries[k], tmp) |
|---|
| 313 | else: |
|---|
| 314 | mpq_set(z.entries[k], w.entries[j]) |
|---|
| 315 | z.positions[k] = w.positions[j] |
|---|
| 316 | k = k + 1 |
|---|
| 317 | j = j + 1 |
|---|
| 318 | else: # equal, so add and copy |
|---|
| 319 | if do_multiply: |
|---|
| 320 | # This means: tmp = v.entries[i] + multiple*w.entries[j] |
|---|
| 321 | mpq_mul(tmp, multiple, w.entries[j]) |
|---|
| 322 | mpq_add(tmp, tmp, v.entries[i]) |
|---|
| 323 | else: |
|---|
| 324 | mpq_add(tmp, v.entries[i], w.entries[j]) |
|---|
| 325 | if mpq_sgn(tmp) != 0: |
|---|
| 326 | z.positions[k] = v.positions[i] |
|---|
| 327 | # This means: z.entries[k] = tmp |
|---|
| 328 | mpq_set(z.entries[k], tmp) |
|---|
| 329 | k = k + 1 # only increment if sum is nonzero! |
|---|
| 330 | i = i + 1 |
|---|
| 331 | j = j + 1 |
|---|
| 332 | #end if |
|---|
| 333 | # end while |
|---|
| 334 | z.num_nonzero = k |
|---|
| 335 | mpq_clear(tmp) |
|---|
| 336 | return 0 |
|---|
| 337 | |
|---|
| 338 | cdef int scale_mpq_vector(mpq_vector* v, mpq_t scalar) except -1: |
|---|
| 339 | if mpq_sgn(scalar) == 0: # scalar = 0 |
|---|
| 340 | clear_mpq_vector(v) |
|---|
| 341 | init_mpq_vector(v, v.degree, 0) |
|---|
| 342 | return 0 |
|---|
| 343 | cdef int i |
|---|
| 344 | for i from 0 <= i < v.num_nonzero: |
|---|
| 345 | # v.entries[i] = scalar * v.entries[i] |
|---|
| 346 | mpq_mul(v.entries[i], v.entries[i], scalar) |
|---|
| 347 | return 0 |
|---|
| 348 | |
|---|
| 349 | cdef int mpq_vector_cmp(mpq_vector* v, mpq_vector* w): |
|---|
| 350 | if v.degree < w.degree: |
|---|
| 351 | return -1 |
|---|
| 352 | elif v.degree > w.degree: |
|---|
| 353 | return 1 |
|---|
| 354 | cdef int i, c |
|---|
| 355 | for i from 0 <= i < v.num_nonzero: |
|---|
| 356 | c = mpq_cmp(v.entries[i], w.entries[i]) |
|---|
| 357 | if c < 0: |
|---|
| 358 | return -1 |
|---|
| 359 | elif c > 0: |
|---|
| 360 | return 1 |
|---|
| 361 | return 0 |
|---|