meankondo/src/number.c
Ian Jauslin 167980ea43 Update to v1.5.
The update to version 1.5 is rather substantial, and introduces some minor
  backward-incompatibilities:
    * The header "#!symbols" has been replaced by "#!virtual_fields"
    * Multiplying polynomials using the '*' symbol is no longer supported (or,
      rather, the symbolic capabilities of meankondo were enhanced, and the
      syntax has been changed).
    * 'meantools exp' has been removed (its functionality is now handled by
      other means)
    * 'meantoolds derive' has been replaced by 'meantools differentiate'

  * The symbolic capabilities were enhanced: polynomials can now be
    multiplied, added, exponentiated, and their logarithms can be taken
    directly in the configuration file.

  * The flow equation can now be processed after being computed using the
    various "#!postprocess_*" entries.

  * Deprecated kondo_preprocess.

  * Compute the mean using an LU decomposition if possible.

  * More detailed checks for syntax errors in configuration file.

  * Check that different '#!group' entries are indeed uncorrelated.

  * New flags in meankondo: '-p' and '-A'.

  * New tool: meantools expand.

  * Improve conversion to LaTeX using meantools-convert

  * Assign terms randomly to different threads.

  * Multiple bug fixes
2022-06-14 09:26:07 +02:00

707 lines
16 KiB
C

/*
Copyright 2015-2022 Ian Jauslin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "number.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
// define MPFR_USE_VA_LIST to enable the use of mpfr_inits and mpfr_clears
#define MPFR_USE_VA_LIST
#include <mpfr.h>
#include "istring.h"
#include "definitions.cpp"
#include "tools.h"
#include "rational.h"
#include "array.h"
#include "parse_file.h"
// init
int init_Number(Number* number, int memory){
(*number).scalars=calloc(memory,sizeof(Q));
(*number).base=calloc(memory,sizeof(int));
(*number).memory=memory;
(*number).length=0;
return(0);
}
int free_Number(Number number){
free(number.scalars);
free(number.base);
return(0);
}
// copy
int number_cpy(Number input, Number* output){
init_Number(output,input.length);
number_cpy_noinit(input,output);
return(0);
}
int number_cpy_noinit(Number input, Number* output){
int i;
if((*output).memory<input.length){
fprintf(stderr,"error: trying to copy a number of length %d to another with memory %d\n",input.length, (*output).memory);
exit(-1);
}
for(i=0;i<input.length;i++){
(*output).scalars[i]=input.scalars[i];
(*output).base[i]=input.base[i];
}
(*output).length=input.length;
return(0);
}
// resize memory
int number_resize(Number* number, int newsize){
Number new_number;
init_Number(&new_number, newsize);
number_cpy_noinit(*number,&new_number);
free_Number(*number);
*number=new_number;
return(0);
}
// add a value
int number_append(Q scalar, int base, Number* output){
if((*output).length>=(*output).memory){
number_resize(output,2*(*output).memory);
}
(*output).scalars[(*output).length]=scalar;
(*output).base[(*output).length]=base;
(*output).length++;
// not optimal
number_sort(*output,0,(*output).length-1);
return(0);
}
// concatenate
int number_concat(Number input, Number* output){
int i;
int offset=(*output).length;
if((*output).length+input.length>(*output).memory){
// make it longer than needed by (*output).length (for speed)
number_resize(output,2*(*output).length+input.length);
}
for(i=0;i<input.length;i++){
(*output).scalars[offset+i]=input.scalars[i];
(*output).base[offset+i]=input.base[i];
}
(*output).length=offset+input.length;
return(0);
}
// special numbers
Number number_zero(){
Number ret;
// don't allocate 0 memory since zero's will usually be used to add to
init_Number(&ret,NUMBER_SIZE);
return(ret);
}
Number number_one(){
Number ret=number_zero();
number_add_elem(quot(1,1),1,&ret);
return(ret);
}
// find a base element
int number_find_base(int base, Number number){
return(intlist_find(number.base, number.length, base));
}
// sort
int number_sort(Number number, int begin, int end){
int i;
int index;
// the pivot: middle of the array
int pivot=(begin+end)/2;
// if the array is non trivial
if(begin<end){
// send pivot to the end
number_exchange_terms(pivot, end, number);
// loop over the others
for(i=begin, index=begin;i<end;i++){
// compare with pivot
if(number.base[i]<number.base[end]){
// if smaller, exchange with reference index
number_exchange_terms(i, index, number);
// move reference index
index++;
}
}
// put pivot (which we had sent to the end) in the right place
number_exchange_terms(index, end, number);
// recurse
number_sort(number, begin, index-1);
number_sort(number, index+1, end);
}
return(0);
}
// exchange terms (for sorting)
int number_exchange_terms(int index1, int index2, Number number){
Q qtmp;
int tmp;
qtmp=number.scalars[index1];
number.scalars[index1]=number.scalars[index2];
number.scalars[index2]=qtmp;
tmp=number.base[index1];
number.base[index1]=number.base[index2];
number.base[index2]=tmp;
return(0);
}
// checks whether two numbers are equal
// requires both numbers to be sorted
int number_compare(Number x1, Number x2){
int i;
if(x1.length!=x2.length){
return(0);
}
for(i=0;i<x1.length;i++){
if(x1.base[i]!=x2.base[i] || Q_cmp(x1.scalars[i],x2.scalars[i])!=0){
return(0);
}
}
return(1);
}
// add (write result to second element)
int number_add_chain(Number input, Number* inout){
int i;
for(i=0;i<input.length;i++){
number_add_elem(input.scalars[i], input.base[i], inout);
}
return(0);
}
// add a single element
int number_add_elem(Q scalar, int base, Number* inout){
int index;
index=number_find_base(base,*inout);
if(index>=0){
(*inout).scalars[index]=Q_add((*inout).scalars[index], scalar);
}
else{
number_append(scalar, base, inout);
}
return(0);
}
// create a new number
int number_add(Number x1, Number x2, Number* out){
number_cpy(x1,out);
number_add_chain(x2,out);
return(0);
}
// return the number
Number number_add_ret(Number x1, Number x2){
Number out;
number_add(x1,x2,&out);
return(out);
}
// multiply
int number_prod(Number x1, Number x2, Number* out){
int i,j;
int div;
Q new_scalar;
int new_base;
init_Number(out, x1.length);
for(i=0;i<x1.length;i++){
for(j=0;j<x2.length;j++){
new_scalar=Q_prod(x1.scalars[i], x2.scalars[j]);
// simplify the base
div=gcd(x1.base[i], x2.base[j]);
new_base=(x1.base[i]/div)*(x2.base[j]/div);
new_scalar.numerator*=div;
number_add_elem(new_scalar, new_base, out);
}
}
return(0);
}
// write to second number
int number_prod_chain(Number input, Number* inout){
Number tmp;
number_prod(input,*inout,&tmp);
free_Number(*inout);
*inout=tmp;
return(0);
}
// return result
Number number_prod_ret(Number x1, Number x2){
Number ret;
number_prod(x1,x2,&ret);
return(ret);
}
// multiply by a rational
int number_Qprod_chain(Q q, Number* inout){
int i;
for(i=0;i<(*inout).length;i++){
(*inout).scalars[i]=Q_prod(q,(*inout).scalars[i]);
}
return(0);
}
// write to output
int number_Qprod(Q q, Number x, Number* inout){
number_cpy(x,inout);
number_Qprod_chain(q,inout);
return(0);
}
// return result
Number number_Qprod_ret(Q q, Number x){
Number ret;
number_Qprod(q,x,&ret);
return(ret);
}
// quotient of two numbers
// recursively simplify numerator/denominator until denominator only has one term
// the output is set to 'numerator'
// both numerator and denominator may be changed by this function
// this algorithm is not optimal in cases where denominator has several terms, in particular if their bases are large
// it is optimal if denominator only has one term
int number_quot_inplace(Number* numerator, Number* denominator){
Number tmp;
int i,factor;
switch((*denominator).length){
// error
case 0:
fprintf(stderr,"error: attempting to invert 0\n");
exit(-1);
break;
// trivial case
case 1:
// trivial base
if((*denominator).base[0]==1){
number_Qprod_chain(Q_inverse((*denominator).scalars[0]), numerator);
}
// non-trivial base
else{
// set tmp=1/denominator
tmp=number_one();
tmp.base[0]=(*denominator).base[0];
tmp.scalars[0].denominator=(*denominator).scalars[0].numerator*abs((*denominator).base[0]);
if((*denominator).base[0]>0){
tmp.scalars[0].numerator=(*denominator).scalars[0].denominator;
}
else if((*denominator).base[0]<0){
tmp.scalars[0].numerator=-(*denominator).scalars[0].denominator;
}
else{
fprintf(stderr,"error: attempting to invert 0\n");
exit(-1);
}
number_prod_chain(tmp, numerator);
}
break;
default:
// find non-trivial basis
for(i=0;(*denominator).base[i]==1;i++){}
// smallest prime factor of the base
if((*denominator).base[i]<0){
factor=-1;
}
else if((*denominator).base[i]==2){
factor=2;
}
else{
// COMMENT: this is not optimal, but, provided the basis is not too large, this should not be a problem
for(factor=3;is_factor(factor, (*denominator).base[i])==0;factor=factor+2){}
}
// tmp is set to ((terms that k does not divide)-(terms that k divides))
// tmp will be multiplied to numerator and denominator
init_Number(&tmp,(*denominator).length);
// find all terms whose base is a multiple of k
for(i=0;i<(*denominator).length;i++){
if(is_factor(factor, (*denominator).base[i])){
// add to tmp with a - sign
number_add_elem(quot(-(*denominator).scalars[i].numerator,(*denominator).scalars[i].denominator), (*denominator).base[i], &tmp);
}
else{
// add to tmp
number_add_elem((*denominator).scalars[i], (*denominator).base[i], &tmp);
}
}
number_prod_chain(tmp, numerator);
number_prod_chain(tmp, denominator);
free_Number(tmp);
// recurse
number_quot_inplace(numerator, denominator);
}
return(0);
}
// not inplace
int number_quot(Number x1, Number x2, Number* output){
Number numerator, denominator;
number_cpy(x1, &numerator);
number_cpy(x2, &denominator);
number_quot_inplace(&numerator, &denominator);
*output=numerator;
free_Number(denominator);
return(0);
}
int number_quot_chain(Number* inout, Number x2){
Number tmp;
number_quot(*inout,x2,&tmp);
free_Number(*inout);
*inout=tmp;
return(0);
}
Number number_quot_ret(Number x1, Number x2){
Number ret;
number_quot(x1, x2, &ret);
return(ret);
}
// remove 0's
int number_simplify(Number in, Number* out){
int i;
init_Number(out, in.length);
for(i=0;i<in.length;i++){
if(in.scalars[i].numerator!=0){
number_append(in.scalars[i],in.base[i], out);
}
}
return(0);
}
// check whether a number is 0
int number_is_zero(Number x){
int i;
for(i=0;i<x.length;i++){
if(x.scalars[i].numerator!=0 && x.base[i]!=0){
return(0);
}
}
return(1);
}
// approximate numerical expression
long double number_double_val(Number x){
int i;
long double ret=0.;
long double b;
for(i=0;i<x.length;i++){
if(x.scalars[i].numerator!=0){
b=1.0*x.base[i];
ret+=Q_double_value(x.scalars[i])*sqrt(b);
}
}
return(ret);
}
// approximate numerical expression (as mpfr float)
int number_mpfr_val(mpfr_t out, Number x){
int i;
// auxiliary variables (do not initialize A)
mpfr_t A,b,c;
mpfr_inits(b,c, (mpfr_ptr)NULL);
mpfr_init(out);
mpfr_set_zero(out,1);
for(i=0;i<x.length;i++){
if(x.scalars[i].numerator!=0){
mpfr_sqrt_ui(b, x.base[i], MPFR_RNDN);
Q_mpfr_value(A, x.scalars[i]);
mpfr_mul(c, A, b, MPFR_RNDN);
mpfr_add(b, out, c, MPFR_RNDN);
mpfr_set(out, b, MPFR_RNDN);
}
}
mpfr_clears(A,b,c, (mpfr_ptr)NULL);
return(0);
}
// print to string
int number_sprint(Number number, Char_Array* out){
int i;
Number simp;
number_simplify(number, &simp);
for(i=0;i<simp.length;i++){
if(i>0){
char_array_snprintf(out," + ");
}
if(simp.length>1 || (simp.length==1 && simp.base[0]!=1)){
char_array_append('(',out);
}
Q_sprint(simp.scalars[i], out);
if(simp.length>1 || (simp.length==1 && simp.base[0]!=1)){
char_array_append(')',out);
}
if(simp.base[i]!=1){
char_array_snprintf(out,"s{%d}",simp.base[i]);
}
}
free_Number(simp);
return(0);
}
// print to stdout
int number_print(Number number){
Char_Array buffer;
init_Char_Array(&buffer,5*number.length);
number_sprint(number, &buffer);
printf("%s",char_array_to_str_noinit(&buffer));
return(0);
}
#define PP_NULL_MODE 0
#define PP_NUM_MODE 1
#define PP_SQRT_MODE 2
// read from a string
int str_to_Number(char* str, Number* number){
char* ptr;
int mode;
char* buffer=calloc(str_len(str)+1,sizeof(char));
char* buffer_ptr=buffer;
Q num;
int base;
int ret;
int j;
char* aux_str;
int aux_free=0;
init_Number(number, NUMBER_SIZE);
// check whether the string is blank (return 0 in that case)
for(ptr=str;*ptr!='\0';ptr++){
if(*ptr!=' ' && *ptr!='\n'){
break;
}
}
// blank string
if(*ptr=='\0'){
number_add_elem(quot(0,1), 1, number);
free(buffer);
return(0);
}
// init num and base
num=quot(1,1);
base=1;
// check whether the str only contains a rational number, and add parentheses
// keep rtack of the length of str
for(j=0,ptr=str;*ptr!='\0';j++,ptr++){
if((*ptr<'0' || *ptr>'9') && *ptr!='-' && *ptr!='/'){
break;
}
}
// only rational
if(*ptr=='\0'){
aux_str=calloc(j+3,sizeof(char));
aux_str[0]='(';
for(j=0,ptr=str;*ptr!='\0';ptr++,j++){
aux_str[j+1]=*ptr;
}
aux_str[j+1]=')';
aux_str[j+2]='\0';
aux_free=1;
}
else{
aux_str=str;
}
mode=PP_NULL_MODE;
for(ptr=aux_str;*ptr!='\0';ptr++){
switch(*ptr){
// read number
case '(':
if(mode==PP_NULL_MODE){
// init base
base=1;
mode=PP_NUM_MODE;
}
else{
fprintf(stderr,"syntax error: misplaced '(' in number '%s'\n",str);
exit(-1);
}
break;
case ')':
if(mode==PP_NUM_MODE){
str_to_Q(buffer,&num);
buffer_ptr=buffer;
*buffer_ptr='\0';
mode=PP_NULL_MODE;
}
else{
fprintf(stderr,"syntax error: mismatched ')' in number '%s'\n",str);
exit(-1);
}
break;
// read sqrt
case '{':
// init num
if(num.numerator==0){
num=quot(1,1);
}
if(mode==PP_NULL_MODE){
mode=PP_SQRT_MODE;
}
else{
fprintf(stderr,"syntax error: misplaced '{' in number '%s'\n",str);
exit(-1);
}
break;
case '}':
if(mode==PP_SQRT_MODE){
ret=read_int(buffer,&base);
if(ret<0){
fprintf(stderr,"syntax error: number base should be an integer, got '%s' in '%s'",buffer,str);
exit(-1);
}
buffer_ptr=buffer;
*buffer_ptr='\0';
mode=PP_NULL_MODE;
}
else{
fprintf(stderr,"syntax error: mismatched '}' in number '%s'\n",str);
exit(-1);
}
break;
// write num
case '+':
if(mode==PP_NULL_MODE){
number_add_elem(num, base, number);
// re-init num and base
num=quot(0,1);
base=1;
}
else{
fprintf(stderr,"syntax error: misplaced '+' in number '%s'\n",str);
exit(-1);
}
break;
// ignore 's', ' ' and '\n'
case 's':break;
case ' ':break;
case '\n':break;
default:
if(mode!=PP_NULL_MODE){
buffer_ptr=str_addchar(buffer_ptr,*ptr);
}
else{
fprintf(stderr,"syntax error: unrecognized character '%c' in number '%s'\n",*ptr,str);
exit(-1);
}
break;
}
}
if(mode==PP_NULL_MODE){
number_add_elem(num, base, number);
}
else{
fprintf(stderr,"syntax error: mismatched '(' in number '%s'\n",str);
exit(-1);
}
if(aux_free==1){
free(aux_str);
}
free(buffer);
return(0);
}
// with Char_Array input
int char_array_to_Number(Char_Array cstr_num,Number* output){
char* buffer;
char_array_to_str(cstr_num,&buffer);
str_to_Number(buffer, output);
free(buffer);
return(0);
}
// -------------------- Number_Matrix ---------------------
// init
int init_Number_Matrix(Number_Matrix* matrix, int length){
int i,j;
(*matrix).matrix=calloc(length,sizeof(Number*));
(*matrix).indices=calloc(length,sizeof(int));
for(i=0;i<length;i++){
(*matrix).matrix[i]=calloc(length,sizeof(Number));
for(j=0;j<length;j++){
(*matrix).matrix[i][j]=number_zero();
}
}
(*matrix).length=length;
return(0);
}
int free_Number_Matrix(Number_Matrix matrix){
int i,j;
for(i=0;i<matrix.length;i++){
for(j=0;j<matrix.length;j++){
free_Number(matrix.matrix[i][j]);
}
free(matrix.matrix[i]);
}
free(matrix.matrix);
free(matrix.indices);
return(0);
}
// Pauli matrices
int Pauli_matrix(int i, Number_Matrix* output){
init_Number_Matrix(output,2);
switch(i){
case 1:
number_add_elem(quot(1,1),1,(*output).matrix[0]+1);
number_add_elem(quot(1,1),1,(*output).matrix[1]+0);
break;
case 2:
number_add_elem(quot(-1,1),-1,(*output).matrix[0]+1);
number_add_elem(quot(1,1),-1,(*output).matrix[1]+0);
break;
case 3:
number_add_elem(quot(1,1),1,(*output).matrix[0]+0);
number_add_elem(quot(-1,1),1,(*output).matrix[1]+1);
break;
default:
fprintf(stderr,"error: requested %d-th pauli matrix\n",i);
exit(-1);
}
return(0);
}