Exemple #1
0
void defiSite::print(FILE* f) const {
  fprintf(f, "Site '%p' %s\n", name(),
     orientStr());
  fprintf(f, "  DO X %g %g BY %g\n",
     x_orig(),
     x_num(),
     x_step());
  fprintf(f, "  DO Y %g %g BY %g\n",
     y_orig(),
     y_num(),
     y_step());

}
Exemple #2
0
//=============================================================================
// PROCEDURE
//    CostFunction
//-----------------------------------------------------------------------------
// ARGUMENTS
//    mX - The current iteration of the n-dimensional X vector of stockpile 
//       percentages.
//    mAssay - The m x n matrix of assays of the stockpiles. This matrix may
//       contain all the assays including the irrelevant elements.
//    mSoftConstraints - a 4 x m matrix of constraints of the assay elements.
//       The 1st row is a row of minima (greater-than) constraints. The 2nd row
//       is contains the maxima (less-than) constraints. The 3rd and 4th rows
//       contain the constraint weightings (I call alpha's) for the 1st and 2nd
//       rows respectively. If an element of the 3rd or 4th row is 0, the
//       corresponding constraint in the 1st or 2nd row is ignored.
//    mSoftRatios - a p x 5 matrix of ratio constraints where p is the number
//       of constraints. The 1st and 2nd columns are the indices of the
//       dividend (numerator, I call gamma_i) and divisor (denominator, I call
//       gamma_j) of the ratio respectively. 
//       The 3rd column is the ratio constraint (I call r). 
//       The 4th column is the weighting (alpha).
//       The 5th (last) column is a boolean representing whether the ratio
//       constraint is a greater-than (1) or less-than (0) constraint. i.e. if
//       column 5 = 1, then the inequality constraint is:
//             (gamma_i/gamma_j) > r
//    dCost - return value - the calculated cost in.
//    mFirstDeriv - return value - the vector first partial derivative of the
//       cost function at the current vector x.
//-----------------------------------------------------------------------------
// RETURN
//    Success or otherwise of the call.
//=============================================================================
FUNCEXPORT
bool Optimise1stOrder(  double dBlendTonnes,
                        const ZMat & mAssays, 
                        const ZMat & mHardConstraints,
                        const ZMat & mSoftConstraints, 
                        const ZMat & mSoftRatios,
                        ZMat & mX)
{
   bool  bReturn = true;

   ZMat  *pAssays       = NULL;
   ZMat  *px            = NULL;
   ZMat  *pHardMaxs     = NULL;
   ZMat  *pHardMins     = NULL;
   ZMat  *pconstrained  = NULL;

   OptimiseSetLastErr("");


   try
   {
      int   m = mAssays.RowCount();
      int   n_orig = mAssays.ColCount();
      int   n;
      int   nXi;
      int   nRow;

      if (mX.RowCount() != n_orig)
      {
         //printf("X row dimension incompatible with Assay matrix\n");
         OptimiseSetLastErr("X row dimension incompatible with Assay matrix");
		 OptimiseSetLastErr("Last Search Fail: Incompatible Inputs");
         return false;
      }

      ZMat  HardMins_orig(mHardConstraints.Transpose().SubMat(":,0"));  // 1st column is mins.
      ZMat  HardMaxs_orig(mHardConstraints.Transpose().SubMat(":,1"));  // 2nd column is max's

      //% Check. If the sum of the elements of the HardMaxs_orig < 1 or the sum of
      //% the elements of HardMins_orig > 1, we have an unsolvable solution.
      //if (sum(HardMins_orig) > 1) | (sum(HardMaxs_orig) < 1)
      //   error ('Unsolveable problem due to hard constraints');
      //end
      if ((ZMat::Sum(HardMins_orig) > 1) || (ZMat::Sum(HardMaxs_orig) < 1))
      {
         printf("The hard contraints are inconsistent\n");
         OptimiseSetLastErr("The hard contraints are inconsistent");
	     OptimiseSetLastStatus("Last Search Fail: Hard Constraint Inconsistent");
         return false;
      }

      //% Check. If any of the maxs are less than the mins, we also have a
      //% nonsolvable problem.
      //for j = 1:n_orig,
      //   if (HardMins_orig(j) > HardMaxs_orig(j))
      //      error (['Unsolveable problem due to hard constraints for stockpile ', j]);
      //   end
      //end
      for (int j = 0; j < n_orig; j++)
      {
         if (HardMins_orig[j][0] > HardMaxs_orig[j][0])
         {
            //printf("The hard contraints are inconsistent\n");
            OptimiseSetLastErr("The hard contraints are inconsistent");
			OptimiseSetLastStatus("Last Search Fail: Hard Constraint Inconsistent");
            return false;
         }
      }

      //% Start x off with valid values. That is the sum of the elements = 1
      //% and all elements lie between their associated hard minima and maxima.
      //HardRange = HardMaxs_orig - HardMins_orig;
      //temp = (1 - sum(HardMins_orig))/sum(HardRange);
      //x_orig = HardMins_orig + temp*HardRange;
      ZMat HardRange = HardMaxs_orig - HardMins_orig;
      double temp = (1.0 - ZMat::Sum(HardMins_orig))/ZMat::Sum(HardRange);
      ZMat  x_orig(HardMins_orig + HardRange*temp);
      //% DEBUG
      // ZMat  x_orig("[0.4;0.6]");
      //% END DEBUG

      // Do some checks on the SoftConstraints matrix for consistency. In the
      // algorithm we check to see if the weighting is non-zero and, if so, we
      // 

      //% Next we determine which elements of the x vector are fully
      //% constrained. If we find any, we'll reduce the order of the problem.
      //% We store the constraint info in the constrained_orig vector below, one
      //% for each element of the x vector. 0 is not constrained, 1 is 
      //% constrained at a minima and 2 is constrained at a maxima. If 
      //% minima = maxima, then the value will be 3.
      //constrained_orig = zeros(n_orig,1);
      //constrained_count = 0;
      //for index = 1:n_orig,
      //    if ((HardMaxs_orig(index) - HardMins_orig(index)) < epsilon)
      //        constrained_orig(index) = 3;
      //        constrained_count = constrained_count + 1;
      //    end
      //end
      ZMat  constrained_orig(n_orig, 1, 0.0);
      int   constrained_count = 0;
      for (int index = 0; index < n_orig; index++)
      {
         if ((HardMaxs_orig[index][0] - HardMins_orig[index][0]) < dEpsilon)
         {
            constrained_orig[index][0] = 3;
            constrained_count++;
         }  // IF
      }


      if (constrained_count > 0)
      {
         // % we need to reduce the order of the problem
         // n = n_orig - constrained_count;
         // nXi = 1;
         // assays = zeros(m,n);
         // x = zeros(n,1);
         // HardMaxs = ones(n,1);
         // HardMins = zeros(n,1);
         // constrained = zeros(n,1);

         n           = n_orig - constrained_count;
         nXi         = 0;
         pAssays     = new ZMat(m, n, 0.0);
         px          = new ZMat(n, 1, 0.0);
         pHardMaxs   = new ZMat(n, 1, 1.0);
         pHardMins   = new ZMat(n,1, 0.0);
         pconstrained = new ZMat(n,1, 0.0);

         //for i = 1:n_orig,
         //   if (constrained_orig(i) ~= 3)
         //         x(nXi) = x_orig(i);
         //         assays(:,nXi) = assays_orig(:,i);
         //         HardMaxs(nXi) = HardMaxs_orig(i);
         //         HardMins(nXi) = HardMins_orig(i);
         //         nXi = nXi + 1;
         //   end
         //end
         for (int i = 0; i < n_orig; i++)
            if (constrained_orig[i][0] != 3)
            {
               (*px)[nXi][0] = x_orig[i][0];
               for (nRow = 0; nRow < pAssays->RowCount(); nRow++)
                  (*pAssays)[nRow][nXi] = mAssays[nRow][i];
               (*pHardMaxs)[nXi][0] = HardMaxs_orig[i][0];
               (*pHardMins)[nXi][0] = HardMins_orig[i][0];
               nXi++;
            }
      }
      else
      {
         //n = n_orig;
         //x = x_orig;
         //assays = assays_orig;
         //constrained = constrained_orig;
         //HardMaxs = HardMaxs_orig;
         //HardMins = HardMins_orig;
         n = n_orig;
         px          = new ZMat(x_orig);
         pAssays     = new ZMat(mAssays);
         pconstrained= new ZMat(constrained_orig);
         pHardMaxs   = new ZMat(HardMaxs_orig);
         pHardMins   = new ZMat(HardMins_orig);
      }


      //% Now we begin the iterative search.
      //% First some initial conditions
      //iter = 1;
      //cost_km1 = 1e12;
      //[cost_k, Del] = CostFunction12(x, assays, SoftConstraints, SoftAlphas, SoftRatios, 0);

      //% We record the current maximum step size we will take along the normalised
      //% derivative vector in the negative direction (i.e. towards zero) in:
      //max_step_size = n*1/sqrt(2);
      int      iter = 1;
      double   cost_km1 = 1e12;
      double   cost_k;

      ZMat  mFirstDeriv(n,1,0.0);

      if (!CostFunction(*px, *pAssays, mSoftConstraints, mSoftRatios, cost_k, mFirstDeriv))
      {
         OptimiseSetLastErr("Cost Function Failed");
         bReturn = false;
         goto __FreeMemory;
      }

      //% We record the current maximum step size we will take along the normalised
      //% derivative vector in the negative direction (i.e. towards zero) in:
      double   max_step_size = n*1/sqrt(2.0);
      ZMat     NextStep(n, 1, 0.0);

      // while (abs(cost_k - cost_km1) > epsilon) & (iter < 100)
      while ((fabs(cost_k - cost_km1) > dEpsilon) && (iter < 100))
      {
         //cost_km1 = cost_k;
         //% Calculate the conventional Newton step
         //NextStep = -1*Del;
         cost_km1 = cost_k;
         // % Calculate the conventional Newton step
         NextStep = mFirstDeriv*(-1.0);
           
         //% Next we constrain the step to lying on the x1 + x2 + ... + xn =
         //% 1 surface. To do this, the NextStep vector must have a net sum
         //% of zero.
         //NextStep = NextStep - ones(n,1)*sum(NextStep/n);
         ZMat  ones(n,1,1.0);
         NextStep -= ones*(ZMat::Sum(NextStep)/double(n));
          
         //% Next we move through the step and work out which elements of the
         //% x vector are in constraint. If they are, we work out if the
         //% current step will maintain this constraint and if so we remove
         //% that element from the adjusted step.
         //constraint_count = 0;
         //for index = 1:n,
         //   if (constrained(index) > 0)
         //         if (constrained(index) == 3)
         //            NextStep(index) = 0;
         //            constraint_count = constraint_count + 1;
         //         elseif (constrained(index) == 1) % minima, greater-than
         //            if (NextStep(index) > 0)
         //               constrained(index) = 0;
         //            else
         //               NextStep(index) = 0;
         //               constraint_count = constraint_count + 1;
         //            end
         //         elseif (constrained(index) == 2) % maxima, less-than
         //            if (NextStep(index) < 0)
         //               constrained(index) = 0;
         //            else
         //               NextStep(index) = 0;
         //               constraint_count = constraint_count + 1;
         //            end
         //         end
         //   end
         int   constraint_count = 0;
         for (int index = 0; index < n; index++)
         {
            int   nConstraintType = Round((*pconstrained)[index][0]);
            switch(nConstraintType)
            {
            case 3:
               NextStep[index][0] = 0;
               constraint_count++;
               break;
            case 1:  //  minima, greater-than
               if (NextStep[index][0] > 0)
                  (*pconstrained)[index][0] = 0;
               else
               {
                  NextStep[index][0] = 0;
                  constraint_count++;
               }
               break;
            case 2:  // maxima, less-than
               if (NextStep[index][0] < 0)
                  (*pconstrained)[index][0] = 0;
               else
               {
                  NextStep[index][0] = 0;
                  constraint_count++;
               }
               break;
            case 0:
            default:
               break;
            }  // SWITCH
         }  // FOR
          
         // % If all elements are in constraint, we terminate.
         //if (constraint_count >= (n-1))
         //   break;  
         //end
         if (constraint_count >= (n-1))
		 {
		  //LogWarning("Optimise1stOrder", 0,"Optimiser Exiting At Iteration %d: All elements in constraint",iter);
			OptimiseSetLastErr("All elements in constraint");
			OptimiseSetLastStatus("Last Search Fail: Exit on All Elements under Constraint");
			bReturn = false;
            break;  
		 }
          
         //% We correct those elements of the NextStep that correspond to
         //% elements of the x vector not in constraint, so that the
         //% NextStep vector again complies with the equality condition.
         //correction = sum(NextStep)/(n - constraint_count);
         //for index = 1:n,
         //   if (constrained(index) == 0) % not constrained
         //         NextStep(index) = NextStep(index) - correction;
         //   end
         //end
         double correction = ZMat::Sum(NextStep)/double(n - constraint_count);
         for (index = 0; index < n; index++)
            if ((*pconstrained)[index][0] == 0)  // % not constrained
               NextStep[index][0] -= correction;
          
         //% If the NextStep at this point is so pathetically small as to generate
         //% significant errors because of rounding problems, we call it a day.
         //significant_element_found = 0;
         //for index = 1:n,
         //   if (abs(NextStep(index)) > epsilon)
         //         significant_element_found = 1;
         //         break;
         //   end
         //end
         //if (significant_element_found == false)
         //   break;  % Terminate the optimisation search.
         //end
         bool  significant_element_found = false;
         for (index = 0; index < n; index++)
         {
            if (fabs(NextStep[index][0]) > dEpsilon)
            {
               significant_element_found = true;
               break;
            }
         }
         if (!significant_element_found)
		 {
		  ////LogWarning("Optimise1stOrder", 0,"Optimiser next step to small - search complete"
			OptimiseSetLastStatus("Last Search Fail: Step Too Small");
            break;  // Terminate the optimisation search.
		 } 
         // Next we normalise the NextStep vector.
         // NextStep = NextStep/sqrt(NextStep'*NextStep);
         NextStep /= sqrt((NextStep.Transpose()*NextStep)[0][0]);

         //% Next we work how far we can go before out current vector runs into a
         //% hard constraint. We will use gamma as the scalar multiplier of the
         //% NextStep vector to specify the distance we'll move. This will be
         //% constrained to the range 0 to gamma_max where gamma_max corresponds
         //% to the movement along the vector to the nearest constraint, or to
         //% max_step_size.
         //gamma_max = max_step_size; 
         //for index = 1:n,
         //   if (constrained(index) == 0) % Not currently constrained.
         //         if (abs(NextStep(index)) > epsilon)
         //            if (NextStep(index) > 0)
         //               this_gamma_max = (HardMaxs(index) - x(index))/NextStep(index);
         //            else % NextStep(index) < 0
         //               this_gamma_max = (HardMins(index) - x(index))/NextStep(index);
         //            end
         //            if (this_gamma_max < gamma_max)
         //               gamma_max = this_gamma_max;
         //            end
         //         end
         //   end
         //end
         double   gamma_max = max_step_size; 
         double   this_gamma_max;
         for (index = 0; index < n; index++)
         {
            if ((*pconstrained)[index][0] == 0) //  % Not currently constrained.
            {
               if (fabs(NextStep[index][0]) > dEpsilon)
               {
                  if (NextStep[index][0] > 0)
                     this_gamma_max = ((*pHardMaxs)[index][0] - (*px)[index][0])/NextStep[index][0];
                  else //  NextStep(index) < 0
                     this_gamma_max = ((*pHardMins)[index][0] - (*px)[index][0])/NextStep[index][0];

				  if (this_gamma_max < dEpsilon)
			      {
				   // TO DO: Work out actual stockpile number from index
				   //LogWarning("Optimise1stOrder", 0,"Optimiser running into hard constraint for non-empty stockpile %d",index);
				  }

                  if (this_gamma_max < gamma_max)
				  {
                     gamma_max = this_gamma_max;
				  }

               }  // IF
            }
         }  // FOR

         // If we find the maximum step we can take is no step at all, we
         // terminate.
         if (gamma_max < dEpsilon)
		 {
      //LogWarning("Optimise1stOrder", 0,"Optimiser Exiting At Iteration %d: Running into hard constraint",iter);
      OptimiseSetLastErr("Running into hard constraint");
			OptimiseSetLastStatus("Last Search Fail: Exit on Hard Constraint");
			bReturn = false;
            break;
		 }
          
         //% If the NextStep at this point is so pathetically small as to generate
         //% significant errors because of rounding problems, we call it a day.
         //significant_element_found = 0;
         //for index = 1:n,
         //   if (abs(NextStep(index)) > epsilon)
         //         significant_element_found = 1;
         //         break;
         //   end
         //end
         //if (significant_element_found == false)
         //   break;  % Terminate the optimisation search.
         //end
         significant_element_found = false;
         for (index = 0; index < n; index++)
         {
            if (fabs(NextStep[index][0]) > dEpsilon)
            {
               significant_element_found = true;
               break;
            }
         }
         if (!significant_element_found)
		 {
	    ////LogWarning("Optimise1stOrder", 0,"Optimiser next step to small - exiting"
      OptimiseSetLastErr("Step Too Small");
			OptimiseSetLastStatus("Last Search Fail: Step Too Small");
			bReturn = false;
            break;  // Terminate the optimisation search.
		 }
         //% Now we implement a Golden section search. We currently have two
         //% bounds. The current position x, and x + NextStep*gamma_max. We want
         //% to find the value of gamma <= gamma_max that minimises the cost
         //% function.
         //gamma_lo = 0;
         //gamma_hi = gamma_max;
         //gamma_range = gamma_max;
         //gamma_1 = gamma_hi - gamma_range*golden_ratio;
         //gamma_2 = gamma_lo + gamma_range*golden_ratio;
         //cost_lo = cost_k;
         //cost_hi = CostFunction1(x + gamma_hi*NextStep, assays, SoftConstraints, SoftAlphas, SoftRatios, 0);
         //cost_1 =  CostFunction1(x + gamma_1 *NextStep, assays, SoftConstraints, SoftAlphas, SoftRatios, 0);
         //cost_2 =  CostFunction1(x + gamma_2 *NextStep, assays, SoftConstraints, SoftAlphas, SoftRatios, 0);
         double   gamma;
         double   gamma_lo = 0;
         double   gamma_hi = gamma_max;
         double   gamma_range = gamma_max;
         double   gamma_1 = gamma_hi - gamma_range*golden_ratio;
         double   gamma_2 = gamma_lo + gamma_range*golden_ratio;
         double   cost_lo = cost_k;
         double   cost_hi;
         double   cost_1;
         double   cost_2;
         CostFunction((*px) + NextStep*gamma_hi, *pAssays, mSoftConstraints, mSoftRatios, cost_hi);
         CostFunction((*px) + NextStep*gamma_1 , *pAssays, mSoftConstraints, mSoftRatios, cost_1);
         CostFunction((*px) + NextStep*gamma_2 , *pAssays, mSoftConstraints, mSoftRatios, cost_2);

         //for i = 0:20,
         //   if (cost_1 < cost_2)
         //         gamma_hi = gamma_2;
         //         cost_hi = cost_2;
         //         gamma_range = gamma_hi - gamma_lo;
         //         gamma_2 = gamma_1;
         //         cost_2 = cost_1;
         //         gamma_1 = gamma_hi - gamma_range*golden_ratio;
         //         cost_1 = CostFunction1(x + gamma_1 *NextStep, assays, SoftConstraints, SoftAlphas, SoftRatios, 0);
         //   else
         //         gamma_lo = gamma_1;
         //         cost_lo = cost_1;
         //         gamma_range = gamma_hi - gamma_lo;
         //         gamma_1 = gamma_2;
         //         cost_1 = cost_2;
         //         gamma_2 = gamma_lo + gamma_range*golden_ratio;
         //         cost_2 = CostFunction1(x + gamma_2 *NextStep, assays, SoftConstraints, SoftAlphas, SoftRatios, 0);
         //   end
         //end
         for (int i = 0; i < 20; i++)
         {
            if (cost_1 < cost_2)
            {
               gamma_hi = gamma_2;
               cost_hi = cost_2;
               gamma_range = gamma_hi - gamma_lo;
               gamma_2 = gamma_1;
               cost_2 = cost_1;
               gamma_1 = gamma_hi - gamma_range*golden_ratio;
               CostFunction((*px) + NextStep*gamma_1, *pAssays, mSoftConstraints, mSoftRatios, cost_1);
            }
            else
            {
               gamma_lo = gamma_1;
               cost_lo = cost_1;
               gamma_range = gamma_hi - gamma_lo;
               gamma_1 = gamma_2;
               cost_1 = cost_2;
               gamma_2 = gamma_lo + gamma_range*golden_ratio;
               CostFunction((*px) + NextStep*gamma_2, *pAssays, mSoftConstraints, mSoftRatios, cost_2);
            }
         }
          
         //if (cost_1 < cost_2)
         //   gamma = gamma_1;
         //   cost_k = cost_1;
         //else
         //   gamma = gamma_2;
         //   cost_k = cost_2;
         //end
         if (cost_1 < cost_2)
         {
            gamma = gamma_1;
            cost_k = cost_1;
         }
         else
         {
            gamma = gamma_2;
            cost_k = cost_2;
         }

         //% Just in case the cost function is montonically decreasing in the
         //% direction of the vector, we also check to see if the constrained
         //% limit is in fact the lowest point.
         //if (cost_hi < cost_k) 
         //   gamma = gamma_hi;
         //   cost_k = cost_hi;
         //end
         if (cost_hi < cost_k) 
         {
            gamma = gamma_hi;
            cost_k = cost_hi;
         }
          
         // Now we actually perform the step ...
         // x = x + gamma*NextStep;
         (*px) += NextStep*gamma;
          
         //% ... and we re-call the CostFunction since we need the first
         //% derivative.
         //[cost_k, Del] = CostFunction12(x, assays, SoftConstraints, SoftAlphas, SoftRatios, 0);
         CostFunction(*px, *pAssays, mSoftConstraints, mSoftRatios, cost_k, mFirstDeriv);
          
         //% Reduce the maximum step size by the maximum of 2/3 of the current max
         //% size or the actual step taken.
         //max_step_size = max(max_step_size*0.2, gamma);
         max_step_size = Max(max_step_size*0.2, gamma);

         // Now we update the constraint vector to reflect the new constraint status.
         for (int index = 0; index < n; index++)
         {
            double   dx = (*px)[index][0];
            (*pconstrained)[index][0] = 0;
            if (dx <= ((*pHardMins)[index][0] + dEpsilon))
            {
               if (dx >= ((*pHardMaxs)[index][0] - dEpsilon))
                  (*pconstrained)[index][0] = 3;
               else
                  (*pconstrained)[index][0] = 1;
            }
            else if (dx >= ((*pHardMaxs)[index][0] - dEpsilon))
               (*pconstrained)[index][0] = 2;
         }  // FOR

         iter++;
      }  //% WHILE

      // Now we reconstruct the original x vector
      //nXi = 1;
      //for i = 1:n_orig,
      //   if (constrained_orig(i) ~= 3)
      //      x_orig(i) = x(nXi);
      //      nXi = nXi + 1;
      //   end
      //end
      nXi = 0;
      for (int i = 0; i < n_orig; i++)
      {
         if (constrained_orig[i][0] != 3)
         {
            x_orig[i][0] = (*px)[nXi][0];
            nXi++;
         }  // IF
      }  // FOR

      mX = x_orig;

	  if (iter > 100)
	  {
		//LogWarning("Optimise1stOrder", 0,"Optimiser finished searching in %d iterations",iter);
    OptimiseSetLastErr("Exit after 100 iterations");
    OptimiseSetLastStatus("Last Search Fail: Exit after 100 iterations");
		bReturn = false;
	  }
	  else
	  {
	    if ((fabs(cost_k - cost_km1) <= dEpsilon) && (bReturn))
			OptimiseSetLastStatus("Last Search OK: Converged");
	  }

   }  // TRY
   catch(...)
   {
      OptimiseSetLastErr("An exception occurred during calculation");
      OptimiseSetLastStatus("Last Search Fail: Exit on Exception");
      bReturn = false;
   }

__FreeMemory:
   if (pAssays != NULL)
      delete pAssays;
   if (px != NULL)
      delete px;
   if (pHardMaxs != NULL)
      delete pHardMaxs;
   if (pHardMins != NULL)
      delete pHardMins;
   if (pconstrained != NULL)
      delete pconstrained;

   return bReturn;
}