############################################################## # Perl code to choose problems from the dbase not being worked on # and compute a number of random iterations # Christopher Hillar # # 3 September 2007 # 22 Feb 2007 ############################################################## use ExpProject; # main module for our important functions ############################################################## # Default variable declarations # # my $numiterations = 1; # default NUMBER of PACKET COMPUTATIONS $PROJECT_ID = 3; # default project id to be computed $FLUSH_WORKING = 0; # leave 'working' flags alone in Problem tables $KEEPTEMPFILES = 0; # to delete or not to delete (temp files after computation) $SUPRESSOUTPUT = 0; # to supress MAPLE and SINGULAR output ## $SUPRESSOUTPUT needs to be removed from this; it is no longer relevant # database defaults: $dsn = 'DBI:mysql:test:localhost'; $db_user_name = 'root'; $db_password = 'secant'; $SINGULAR_path = 'Singular'; $MAPLE_path = 'maple'; $MASTER_POINTS_path = 'points.master'; ############################################################## ############################################################ # here we retrieve command line parameters # # -nARG --> NUMBER of PACKET COMPUTATIONS # ARG = 0 signifies compute until dbase empty # EXAMPLE: singcomputer -n10 # DEFAULT: 1 # # -f --> tells program to flush the database before starting instances # this sets the database to its original condition # # -dd --> don't delete temporary files ! deprecated # # # -dn --> send MAPLE and SINGULAR output to /dev/null # # my $numArgs = $#ARGV + 1; my $parameters = ""; foreach my $argnum (0 .. $#ARGV) { $parameters .= $ARGV[$argnum]; } if ( $parameters =~ m/-n(\d*)/ ) { $numiterations = $1; } if ( $parameters =~ m/-f/ ) { $FLUSH_WORKING = 1; } if ( $parameters =~ m/-dd/ ) { $KEEPTEMPFILES = 1; } if ( $parameters =~ m/-dn/ ) { $SUPRESSOUTPUT = 1; } if ( $numiterations > 0 ) { print "You have selected to perform $numiterations iterations\n"; } else { print "You have selected to iterate until all problems in the dbase are done\n"; } ############################################################# ############################################################# # Main Code Block # # uses iteration function defined below # # # # if ( $numiterations > 0 ) { # perform the number of iterations in the command line for ( my $i = 0; $i < $numiterations; $i++ ) { one_iteration(); $FLUSH_WORKING = 0; } } else { # if user wants to empty entire database while ( one_iteration() ) { } } ############################################################## ################################################################ # MAIN PROCEDURE # ################################################################ # int one_iteration($PROJECT_ID,$dsn,$db_user_name,$db_password) # # This is the main heart of the code # This function looks for a free problem in the database # and computes packet size number of calculations # # returns 0 if there are no more problems in the database left # # it assumes $PROJECT_ID,$dsn,$db_user_name,$db_password,$FLUSH_WORKING # from top of this file # sub one_iteration { ############################################################ # these are the standard commands to connect to the database use DBI; my ($id, $password); my $dbh = DBI->connect($dsn, $db_user_name, $db_password); ############################################################ # # first set all problems that have been working for longer than # one day to not working (ie change working flags to 0) # **********************FIX THIS LAZINESS***************************** # for now just set all of them to working = 0 # but really should check for start computation dates older than a day # *********************************************************** if ( $FLUSH_WORKING == 1 ) { my $sth = $dbh->prepare(qq{UPDATE Problem SET started = '0', completed = '0', computationtime='0', instancenumber='0', initialrandseed='0',crossingtable=''}); $sth->execute(); } # search for a problem that is free my $sth = $dbh->prepare(qq{select id,instancenumber, totalnuminstances, initialrandseed,markovloopsize, markovstepsize, computationsize,dimension,flagvariety,enumproblem,multiplicities,numsolutions from Problem where (completed = '0' and project_id = '$PROJECT_ID') }); $sth->execute(); # if there is a problem to do if ( ($id,$instancenumber,$totalnuminstances,$initialrandseed, $markovloopsize,$markovstepsize, $computationsize,$dimension,$flagvariety, $enumproblem,$multiplicities,$numsolutions) = $sth->fetchrow_array() ) { my $problemid = $id; # store the problem id for later # if first time problem worked on: need to initialize random seed my $initseed = int (time ^ $$ ^ unpack "%L*", 'ps axww | gzip'); if ( $initialrandseed != 0 ) { # if problem worked on before find out current srand(initialrandseed); # random seed deterministically for (my $i = 0; $i < $instancenumber; $i++) { $initseed = int(rand(100000000)); } } srand($initseed); # random seed deterministically # tell database that we are working on this problem # and update instance number in Problem table # *********** THRE IS AN ISSUE HERE: WHAT IF ANOTHER COMPUTER *************** # *********** ACCESSES THIS PROBLEM BEFORE INSTANCE NUMBER IS UPDATED??????? ################################################################################### $instancenumber++; if ( $initialrandseed != 0 ) { my $sth2 = $dbh->prepare(qq{update Problem set started='1', instancenumber='$instancenumber' where id='$id'}); $sth2->execute(); } else { my $sth2 = $dbh->prepare(qq{update Problem set started='1', instancenumber='$instancenumber',initialrandseed='$initseed' where id='$id'}); $sth2->execute(); } # create a "running instance" in the dbase indicating we have started computing $thedatetime = gmtime(time); my $sth = $dbh->prepare(qq{ insert into RunningInstance (problem_id,startdate,instancenumber,randseed) values ('$problemid','$thedatetime','$instancenumber','$initseed') }); $sth->execute(); # ************** FIND OUT HOW TO GET LAST INSERT ID ********************** # $runninginstanceid = $dbh->last_insert_id(qw(RunningInstance)); ############################################# # We will now load the master list of points (as rational numbers with , delimeters) # found in points.master. These should be sorted in numeric order # # When we have more dynamic point selection, this will have to be altered # ############################################# @master_points = load_points($MASTER_POINTS_path); ################################################### # We will now make a large SINGULAR file that will # compute $computationsize instances of the problem # # This computes $markovloopsize instances of the problem # Need to add another loop to get $computationsize into the act. # ################################################### use Sys::Hostname; $host = hostname; # get computer host name #Generate a random string for name of computation file my $random_string=&generate_random_string(11); my $singularfilename = "$host/$random_string".'computation.sing'; # Generate a random string for maple file my $random_string=&generate_random_string(11); my $maplefilename = "$host/$random_string".'.maple'; # Generate a random string for maple results output file my $random_string=&generate_random_string(11); my $mapleresultsfilename = "$host/$random_string".'.results'; # Generate a random string for maple results output file my $random_string=&generate_random_string(11); my $timefilename = "$host/$random_string".'.time'; # # This ?always? tries to create a directory? # mkdir($host); # write header of Maple computation file (prototype is nrr.maple) open(MAPLEFILE,">$maplefilename"); print MAPLEFILE "file:=fopen(\"$mapleresultsfilename\",WRITE):\n"; print MAPLEFILE "TIME:=time():\n"; close(MAPLEFILE); open(SINGULARFILE,">$singularfilename"); # write header of SINGULAR computation file (prototype is computation.sing) print SINGULARFILE "LIB \"schubert.lib\";\n"; print SINGULARFILE "system(\"--ticks-per-sec\", 1000);\n"; print SINGULARFILE "int T = timer;\n"; # put the computationPreamble.sing at top of temp file # for computation reasons, this is faster than loading the lines from a file print SINGULARFILE "intvec a = $flagvariety;\n"; print SINGULARFILE "int n = $dimension;\n"; my $count = 1; my $conditionstring = ""; # parse enum problem while ($enumproblem =~ m/\[([^\]]*)\]/g) { print SINGULARFILE "intvec w$count = $1;\n"; $minus1 = $count-1; $count++; if ( $minus1 > 0 ) { $conditionstring .= "w$minus1, "; } } $minus1 = $count - 1; $conditionstring .= "w$minus1"; print SINGULARFILE "list conditions = $conditionstring;\n"; print SINGULARFILE "intvec multiplicities = $multiplicities;\n"; print SINGULARFILE "int degEP = $numsolutions;\n"; print SINGULARFILE "def R = flagRing(a,n);\n"; print SINGULARFILE "setring R;\n"; print SINGULARFILE "ideal I;\n"; print SINGULARFILE "poly e;\n"; print SINGULARFILE "list points;\n"; print SINGULARFILE "int i;\n"; #################################################################### # begin iterating through $markovloopsize instances of the # problem by changing the points chosen. # # this is accomplished by choosing a random subset of points from # a master (ordered) list @master_points # # first choose the correct number of random points from master list for this problem # (Recall that $multiplicities is stored as a string such as "3,4,5") # # Old: # my $numpointsneeded = ($dimension-1)*addstring($multiplicities); # # Frank's new one # my ($numpointsneeded, $multarray_r) = get_partition($enumproblem, $multiplicities); my @randpoints = @{randSubset(\@master_points,$numpointsneeded)}; ####################################################### # Permutation array # # we fill an initial permutation array with the identity # later we will do a markov chain starting with this permutation # so that we can catalogue what happens with higher crossing numbers # # this array tells us how to send the order of @randpoints # # Ex: @randpoints = (1/2,-1,4) # @permutation = (2, 0, 1) # we will perform a computation with the list of points # being list points = 4, 1/2, -1; # my @permutation; for my $p (0 .. ($numpointsneeded-1)) { $permutation[$p] = $p; } ####################################################### for ( my $i = 1; $i <= $markovloopsize; $i++ ) { # # compute the crossing number for this selection of points # my @multarray; # while ($multiplicities =~ m/([0-9]+)/g) { # for (my $j = 0; $j < $1; $j++) { # push(@multarray,($dimension-1)); # } # } # my $arr = setpartitions_to_arrays(\@permutation, \@multarray); my $arr = setpartitions_to_arrays(\@permutation, $multarray_r); my $tempcn = fast_cross_number($arr,$numpointsneeded); ############################################################ # Now we must place the points ################################# $pointstring = "points = "; for (my $k = 0; $k < $#randpoints; $k++) { $pointstring .= $randpoints[$permutation[$k]].","; } $pointstring .= "$randpoints[$permutation[$#randpoints]];\n"; print SINGULARFILE $pointstring; # now we must place the rest of the SINGULAR code into the file print SINGULARFILE " I = std(flagIdeal(points,conditions,multiplicities,a,n));\n"; print SINGULARFILE " e = 0; \n i=1;\n"; print SINGULARFILE " while (deg(e)<>degEP and i<=nvars(R)) {\n"; print SINGULARFILE " e = univarpol(I,i); \n"; print SINGULARFILE " i++; \n"; print SINGULARFILE " } \n if (i<=nvars(R)) { \n"; print SINGULARFILE " write(\":a $maplefilename\", \"e := \",e, \":\");\n"; print SINGULARFILE " write(\":a $maplefilename\", \"n:=nops(realroot(e,1/1000)):\");\n"; print SINGULARFILE " write(\":a $maplefilename\", \"fprintf(file,\\\" %d,$tempcn \\\",n): \");\n"; print SINGULARFILE " } else { \n"; print SINGULARFILE " write(\":a $maplefilename\",\"fprintf(file,\\\" 0,-1 \\\"):\" );\n }\n"; ############################################################ # Finally we transform the permutation array # so that we can see what happens when altering the crossing number # (note: markovstepsize is taken from the problem table in the dbase) # @permutation = @{next_markov(\@permutation,$markovstepsize)}; } print SINGULARFILE "write(\":a $maplefilename\" , "; print SINGULARFILE "\"TIME:=trunc(1000*(time()-TIME)+\" , timer-T , \"):\");\n"; print SINGULARFILE "write(\":a $maplefilename\",\"fclose(file);\");\n"; print SINGULARFILE "quit;\n"; close(SINGULARFILE); # now run singular file my $status = system("nice -n20 $SINGULAR_path -q < $singularfilename"); # Put tail into maple file open(MAPLEFILE,">>$maplefilename"); print MAPLEFILE "file:=fopen(\"$timefilename\",WRITE):\n"; print MAPLEFILE "fprintf(file, \" %d\\n\", TIME):\n"; print MAPLEFILE "fclose(file):\n"; close(MAPLEFILE); # now run maple file my $status = system("nice -n20 $MAPLE_path -q < $maplefilename"); ####################################################### # At the end of this whole process # # the file $mapleresultsfilename has the # actual #s of solutions for all the $computationsize problems computed # as a sequence of [number of solutions, crossing number] # e.g. 4,0 4,2 4,2 2,4 2,6 4,4 # # We need to recover these integers and add them to the # real solutions table that is in the database for this problem # # 0 1 2 ... (crossing number) # \--------------------- # 0 | # 1 | (#instances with this num real solns and this cross num) # ..| # (num real solutions) # # %solutiontable is a hashtable with 2 keys and one value: # # $solutiontable{r}{c} = the number of instances with real solutions r # and crossing number c ####################################################### # retrieve the number of solutions/cross number from the maple results file my %solutiontable = load_maple_results("$mapleresultsfilename"); # next check if conjecture failed: numsolutions for cross number 0 is less than should be foreach my $j (keys %solutiontable) { if ( ($j < $numsolutions) && $solutiontable{$j}{0} ) { # if conjecture failed for some reason my $sth = $dbh->prepare(qq{ insert into ConjectureDiscrepancy (problem_id) values ('$problemid') }); $sth->execute(); } } # now retrieve table from the problem table in the dbase my $sth = $dbh->prepare(qq{select crossingtable from Problem where (id = '$problemid' and project_id = '$PROJECT_ID') }); $sth->execute(); ($oldsolutiontable) = $sth->fetchrow_array(); # this is sored as a string # finally update the database with the new table if ( length($oldsolutiontable) < 3 ) { # if table empty simply stick new table in dbase my $newtable = solutions_table_to_string(\%solutiontable); my $sth = $dbh->prepare(qq{UPDATE Problem SET crossingtable = '$newtable' WHERE id ='$problemid' }); $sth->execute(); } else { # need to merge the two tables my %oldhashtable = load_dbase_solutions_table($oldsolutiontable); my %mergedtable = merge_two_solutions_table(\%oldhashtable,\%solutiontable); my $mergedstring = solutions_table_to_string(\%mergedtable); my $sth = $dbh->prepare(qq{UPDATE Problem SET crossingtable = '$mergedstring' WHERE id ='$problemid' }); $sth->execute(); } # now retrieve computation time from the problem table in the dbase my $sth = $dbh->prepare(qq{select computationtime from Problem where (id = '$problemid' and project_id = '$PROJECT_ID') }); $sth->execute(); $computationtime = $sth->fetchrow_array(); open(TIMEFILE, "<$timefilename"); chomp($line = ); $computationtime = $computationtime+$line; my $sth = $dbh->prepare(qq{UPDATE Problem SET computationtime = '$computationtime' WHERE id ='$problemid' }); $sth->execute(); ####################################################### # # Erase temporary files unless the -dd option is used # ####################################################### if ( !$KEEPTEMPFILES ) { unlink($singularfilename); unlink($maplefilename); unlink($mapleresultsfilename); unlink($timefilename); } ####################################################### # Update dbase to remove this running instance # and the close problem if finished all instances ####################################################### my $sth = $dbh->prepare(qq{ delete from RunningInstance where problem_id='$problemid' and instancenumber='$instancenumber' }); $sth->execute(); if ( $instancenumber >= $totalnuminstances ) { my $sth = $dbh->prepare(qq{UPDATE Problem SET completed = '1' WHERE id ='$problemid' }); $sth->execute(); } } else { # no problems left in the database return 0; } return 1; } ############################################################################################## ############################################################# # # (scalar, array reference) = get_partition( enumerative problem, multiplicities ) # # determines the total number of points needed for this enumerative problem # (the scalar) and the underlying partition for the points (the referenced array) # sub get_partition { my ($ep, $mult) = @_; my @num_points_list = &n_points_list($ep); my @partition = (); my $j = 0; my $sum = 0; while ( $mult =~ m/([0-9]+)/g) { for ( my $i = 1; $i <= $1; $i++ ) { $sum = $sum + $num_points_list[$j]; push(@partition, $num_points_list[$j]); } $j++; } return ($sum,\@partition); } ############################################################# ############################################################# # # (array) = n_points_list( enumerative problem ) # # This returns a list whose i-th entry is the number of points # needed by the i-th condition in the enumerative problem. # #$enumproblem = "[1,3,5,2,3,6],[1,2,4,3,5,6]"; #print "enumerative problem = $enumproblem\n"; #my $format = "points needed = " . ("%2d " x @num_points_list) . "\n"; #printf $format, @num_points_list; # sub n_points_list { my $ep = $_[0]; my @num_list=(); # parse enumerative problem for its permutations while ($ep =~ m/\[([^\]]*)\]/g) { my @perm; my $a = $1; while ($a =~ m/([0-9]+)/g) { push(@perm,$1); } push(@num_list,&points_needed(@perm)); } return @num_list; } ############################################################# ############################################################# # # (scalar) = points_needed( permutation ) # # This computes the number of points needed for the given Schubert # condition, which is passed to the subroutine as the argument. # #@perm = (1,5,8,4,6,3,2,7); #@perm = (1,3,5,2,4,6); #@perm = (1,2,4,3,5,6,7,8); #printf "Number of points needed = %d\n", points_needed(@perm); # sub points_needed { my @perm = @_; my $number_of_points = 0; for (my $des=0; $des < $#perm-1; $des++) { # find the descent of the permutation if ($perm[$des] > $perm[$des+1]) { # @tmp is the schubert condition in the Grassmannian projection my @tmp = sort(@perm[0..$des]); my $j = 0; # Finds the first essential condition while (( $j == $tmp[$j]-1 ) && ( $j < $des ) ) { $j++; } # Sees if this requires more points if ( ( $j <= $des ) && ($number_of_points < $#perm+2-$tmp[$j] ) ) { $number_of_points = $#perm+2-$tmp[$j]; } } } return $number_of_points; } ############################################################################################## ############################################################################################### #############################################################################################