// -------------------------------------------------------------
// PetscNonlinearSolverImplementation::FormFunction
// -------------------------------------------------------------
PetscErrorCode
PetscNonlinearSolverImplementation::FormFunction(SNES snes, Vec x, Vec f, void *dummy)
{
  PetscErrorCode ierr(0);

  // Necessary C cast
  PetscNonlinearSolverImplementation *solver =
    (PetscNonlinearSolverImplementation *)dummy;

  // Apparently, you cannot count on x and f (like in FormJacobian())
  // being the same as those used to set up SNES, so the PETSc
  // solution and function vectors are wrapped temporarily for the
  // user function

  boost::scoped_ptr<Vector> 
    xtmp(new Vector(new PETScVectorImplementation(x, false)));

  boost::scoped_ptr<Vector> 
    ftmp(new Vector(new PETScVectorImplementation(f, false)));

  // Call the user-specified function (object) to form the RHS
  (solver->p_function)(*xtmp, *ftmp);

  xtmp.reset();
  ftmp.reset();

  return ierr;
}
Beispiel #2
0
double IterativeSylvester::performFirstStep(KronVector& x) const
{
	KronVector xtmp((const KronVector&)x);
	KronUtils::multKron(*matrixF, *matrixK, xtmp);
	x.add(-1., xtmp);
	double norm = xtmp.getMax();
	return norm;
}
Beispiel #3
0
double IterativeSylvester::performStep(const QuasiTriangular& k, const QuasiTriangular& f,
									   KronVector& x)
{
	KronVector xtmp((const KronVector&)x);
	KronUtils::multKron(f, k, xtmp);
	x.add(1.0, xtmp);
	double norm = xtmp.getMax();
	return norm;
}
// return  x = (I - op/rhomax)*x in x
void MxGeoMultigridPrec::smoothInterpolation(int level, Epetra_MultiVector & x) const {
  Epetra_MultiVector xtmp(x);

  // get matrix diagonal
  Epetra_Vector diag(ops[level]->RangeMap());
  ops[level]->ExtractDiagonalCopy(diag);
  //diag.Reciprocal(diag);

  ops[level]->Apply(x, xtmp);    // xtmp = op*x
  xtmp.ReciprocalMultiply(1.0, diag, xtmp, 0.0);
  //xtmp.Scale(1.3333 / maxEigs[level]); // xtmp = (op/rhomax)*x
  xtmp.Scale(0.5 * 1.3333); // xtmp = (op/rhomax)*x (rhomax is usually 2)
  x.Update(-1.0, xtmp, 1.0);        // x    = x - xtmp
}
Vector Rosen34(
	Fun           &F , 
	size_t         M , 
	const Scalar &ti , 
	const Scalar &tf , 
	const Vector &xi ,
	Vector       &e )
{
	CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL;

	// check numeric type specifications
	CheckNumericType<Scalar>();

	// check simple vector class specifications
	CheckSimpleVector<Scalar, Vector>();

	// Parameters for Shampine's Rosenbrock method
	// are static to avoid recalculation on each call and 
	// do not use Vector to avoid possible memory leak
	static Scalar a[3] = {
		Scalar(0),
		Scalar(1),
		Scalar(3)   / Scalar(5)
	};
	static Scalar b[2 * 2] = {
		Scalar(1),
		Scalar(0),
		Scalar(24)  / Scalar(25),
		Scalar(3)   / Scalar(25)
	};
	static Scalar ct[4] = {
		Scalar(1)   / Scalar(2),
		- Scalar(3) / Scalar(2),
		Scalar(121) / Scalar(50),
		Scalar(29)  / Scalar(250)
	};
	static Scalar cg[3 * 3] = {
		- Scalar(4),
		Scalar(0),
		Scalar(0),
		Scalar(186) / Scalar(25),
		Scalar(6)   / Scalar(5),
		Scalar(0),
		- Scalar(56) / Scalar(125),
		- Scalar(27) / Scalar(125),
		- Scalar(1)  / Scalar(5)
	};
	static Scalar d3[3] = {
		Scalar(97) / Scalar(108),
		Scalar(11) / Scalar(72),
		Scalar(25) / Scalar(216)
	};
	static Scalar d4[4] = {
		Scalar(19)  / Scalar(18),
		Scalar(1)   / Scalar(4),
		Scalar(25)  / Scalar(216),
		Scalar(125) / Scalar(216)
	};
	CPPAD_ASSERT_KNOWN(
		M >= 1,
		"Error in Rosen34: the number of steps is less than one"
	);
	CPPAD_ASSERT_KNOWN(
		e.size() == xi.size(),
		"Error in Rosen34: size of e not equal to size of xi"
	);
	size_t i, j, k, l, m;             // indices

	size_t  n    = xi.size();         // number of components in X(t)
	Scalar  ns   = Scalar(double(M)); // number of steps as Scalar object
	Scalar  h    = (tf - ti) / ns;    // step size 
	Scalar  zero = Scalar(0);         // some constants
	Scalar  one  = Scalar(1);
	Scalar  two  = Scalar(2);

	// permutation vectors needed for LU factorization routine
	CppAD::vector<size_t> ip(n), jp(n);

	// vectors used to store values returned by F
	Vector E(n * n), Eg(n), f_t(n);
	Vector g(n * 3), x3(n), x4(n), xf(n), ftmp(n), xtmp(n), nan_vec(n);

	// initialize e = 0, nan_vec = nan
	for(i = 0; i < n; i++)
	{	e[i]       = zero;
		nan_vec[i] = nan(zero);
	}

	xf = xi;           // initialize solution
	for(m = 0; m < M; m++)
	{	// time at beginning of this interval
		Scalar t = ti * (Scalar(int(M - m)) / ns) 
		         + tf * (Scalar(int(m)) / ns);

		// value of x at beginning of this interval
		x3 = x4 = xf;

		// evaluate partial derivatives at beginning of this interval
		F.Ode_ind(t, xf, f_t);
		F.Ode_dep(t, xf, E);    // E = f_x
		if( hasnan(f_t) || hasnan(E) )
		{	e = nan_vec;
			return nan_vec;
		}

		// E = I - f_x * h / 2
		for(i = 0; i < n; i++)
		{	for(j = 0; j < n; j++)
				E[i * n + j] = - E[i * n + j] * h / two;
			E[i * n + i] += one;
		}

		// LU factor the matrix E
# ifndef NDEBUG
		int sign = LuFactor(ip, jp, E);
# else
		LuFactor(ip, jp, E);
# endif
		CPPAD_ASSERT_KNOWN(
			sign != 0,
			"Error in Rosen34: I - f_x * h / 2 not invertible"
		);

		// loop over integration steps
		for(k = 0; k < 3; k++)
		{	// set location for next function evaluation
			xtmp = xf; 
			for(l = 0; l < k; l++)
			{	// loop over previous function evaluations
				Scalar bkl = b[(k-1)*2 + l];
				for(i = 0; i < n; i++)
				{	// loop over elements of x
					xtmp[i] += bkl * g[i*3 + l] * h;
				}
			}
			// ftmp = F(t + a[k] * h, xtmp)
			F.Ode(t + a[k] * h, xtmp, ftmp); 
			if( hasnan(ftmp) )
			{	e = nan_vec;
				return nan_vec;
			}

			// Form Eg for this integration step
			for(i = 0; i < n; i++)
				Eg[i] = ftmp[i] + ct[k] * f_t[i] * h;
			for(l = 0; l < k; l++)
			{	for(i = 0; i < n; i++)
					Eg[i] += cg[(k-1)*3 + l] * g[i*3 + l];
			}

			// Solve the equation E * g = Eg
			LuInvert(ip, jp, E, Eg);

			// save solution and advance x3, x4
			for(i = 0; i < n; i++)
			{	g[i*3 + k]  = Eg[i];
				x3[i]      += h * d3[k] * Eg[i];
				x4[i]      += h * d4[k] * Eg[i];
			}
		}
		// Form Eg for last update to x4 only
		for(i = 0; i < n; i++)
			Eg[i] = ftmp[i] + ct[3] * f_t[i] * h;
		for(l = 0; l < 3; l++)
		{	for(i = 0; i < n; i++)
				Eg[i] += cg[2*3 + l] * g[i*3 + l];
		}

		// Solve the equation E * g = Eg
		LuInvert(ip, jp, E, Eg);

		// advance x4 and accumulate error bound
		for(i = 0; i < n; i++)
		{	x4[i] += h * d4[3] * Eg[i];

			// cant use abs because cppad.hpp may not be included
			Scalar diff = x4[i] - x3[i];
			if( diff < zero )
				e[i] -= diff;
			else	e[i] += diff;
		}

		// advance xf for this step using x4
		xf = x4;
	}
	return xf;
}
Beispiel #6
0
/**
 * Description not yet available.
 * \param
 */
void laplace_approximation_calculator::
  do_separable_stuff_laplace_approximation_block_diagonal(df1b2variable& ff)
{
  double w_i;	
  if (pmin->multinomial_weights==0)
  {
    w_i=1.0;
  }
  else
  {
    dvector & w= *(pmin->multinomial_weights);	
    w_i=w[separable_calls_counter];
    //if(separable_calls_counter==938) 
    //  cout << w_i << "____________hansf=" << value(ff) << endl;
    initial_df1b2params::cobjfun+=(w_i-1.0)*value(ff);
  }
  //cout << w_i << " " << separable_calls_counter << "cumulative l (foer det)=" << value(ff) << "CUMULATIV" << initial_df1b2params::cobjfun << endl; 
  
  set_dependent_variable(ff);
  df1b2_gradlist::set_no_derivatives();
  df1b2variable::passnumber=1;
  df1b2_gradcalc1();
   
  init_df1b2vector & locy= *funnel_init_var::py;
  imatrix& list=*funnel_init_var::plist;

  int i; int j; int us=0; int xs=0;
  ivector lre_index(1,funnel_init_var::num_active_parameters);
  ivector lfe_index(1,funnel_init_var::num_active_parameters);

  for (i=1;i<=funnel_init_var::num_active_parameters;i++)
  {
    if (list(i,1)>xsize) 
    {
      lre_index(++us)=i;
    }
    else if (list(i,1)>0) 
    {
      lfe_index(++xs)=i;
    }
  }
  
  dvector local_xadjoint(1,xs);
  for (j=1;j<=xs;j++)
  {
    int j2=list(lfe_index(j),2);
    local_xadjoint(j)=ff.u_dot[j2-1];
  }
  
  if (us>0)
  {
    dmatrix local_Hess(1,us,1,us); 
    dvector local_grad(1,us); 
    dmatrix local_Dux(1,us,1,xs); 
    local_Hess.initialize();
    dvector local_uadjoint(1,us);
    for (i=1;i<=us;i++)
    {
      for (j=1;j<=us;j++)
      {
        int i2=list(lre_index(i),2);
        int j2=list(lre_index(j),2);
        local_Hess(i,j)+=locy(i2).u_bar[j2-1];
      }
    }
    for (i=1;i<=us;i++)
    {
      int i2=list(lre_index(i),2);
      local_uadjoint(i)= ff.u_dot[i2-1];
    }
  
    for (i=1;i<=us;i++)
    {
      for (j=1;j<=xs;j++)
      {
        int i2=list(lre_index(i),2);
        int j2=list(lfe_index(j),2);
        local_Dux(i,j)=locy(i2).u_bar[j2-1];
      }
    }
  
  
    //if (initial_df1b2params::separable_calculation_type==3)
    {
  
    //int nvar=us*us;
    double f;
    dmatrix Hessadjoint=get_gradient_for_hessian_calcs(local_Hess,f);
    initial_df1b2params::cobjfun+=w_i*f;
    //cout << w_i << " " << separable_calls_counter << "cumulative l (etter det)=" << f << "CUMULATIV" << initial_df1b2params::cobjfun <<endl; 
    //cout << "us=" << us << endl;

    // For weighted data we need to add 0.5*log(2pi) here
    if (pmin->multinomial_weights)
      initial_df1b2params::cobjfun-=w_i*us*.91893853320467241;

    for (i=1;i<=us;i++)
    {
      for (j=1;j<=us;j++)
      {
        int i2=list(lre_index(i),2);
        int j2=list(lre_index(j),2);
        locy(i2).get_u_bar_tilde()[j2-1]=Hessadjoint(i,j);
      }
    }
    
     df1b2variable::passnumber=2;
     df1b2_gradcalc1();
  
     df1b2variable::passnumber=3;
     df1b2_gradcalc1();
      dvector xtmp(1,xs);
      xtmp.initialize();
      for (i=1;i<=xs;i++)
      {
        int i2=list(lfe_index(i),2);
        xtmp(i)+=locy[i2].u_tilde[0];
        local_xadjoint(i)+=locy[i2].u_tilde[0];
      }
      dvector utmp(1,us);
      utmp.initialize();
      for (i=1;i<=us;i++)
      {
        int i2=list(lre_index(i),2);
        utmp(i)+=locy[i2].u_tilde[0];
        local_uadjoint(i)+=locy[i2].u_tilde[0];
      }
      if (xs>0)
        local_xadjoint -= local_uadjoint*inv(local_Hess)*local_Dux;
    }
  }
  for (i=1;i<=xs;i++)
  {
    int ii=lfe_index(i);
    xadjoint(list(ii,1))+=w_i*local_xadjoint(i);
  }
  f1b2gradlist->reset();
  f1b2gradlist->list.initialize();
  f1b2gradlist->list2.initialize();
  f1b2gradlist->list3.initialize();
  f1b2gradlist->nlist.initialize();
  f1b2gradlist->nlist2.initialize();
  f1b2gradlist->nlist3.initialize();
  funnel_init_var::num_vars=0;
  funnel_init_var::num_active_parameters=0;
  funnel_init_var::num_inactive_vars=0;
}
Beispiel #7
0
Vector Runge45(
	Fun           &F ,
	size_t         M ,
	const Scalar &ti ,
	const Scalar &tf ,
	const Vector &xi ,
	Vector       &e )
{
	CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL;

	// check numeric type specifications
	CheckNumericType<Scalar>();

	// check simple vector class specifications
	CheckSimpleVector<Scalar, Vector>();

	// Cash-Karp parameters for embedded Runge-Kutta method
	// are static to avoid recalculation on each call and
	// do not use Vector to avoid possible memory leak
	static Scalar a[6] = {
		Scalar(0),
		Scalar(1) / Scalar(5),
		Scalar(3) / Scalar(10),
		Scalar(3) / Scalar(5),
		Scalar(1),
		Scalar(7) / Scalar(8)
	};
	static Scalar b[5 * 5] = {
		Scalar(1) / Scalar(5),
		Scalar(0),
		Scalar(0),
		Scalar(0),
		Scalar(0),

		Scalar(3) / Scalar(40),
		Scalar(9) / Scalar(40),
		Scalar(0),
		Scalar(0),
		Scalar(0),

		Scalar(3) / Scalar(10),
		-Scalar(9) / Scalar(10),
		Scalar(6) / Scalar(5),
		Scalar(0),
		Scalar(0),

		-Scalar(11) / Scalar(54),
		Scalar(5) / Scalar(2),
		-Scalar(70) / Scalar(27),
		Scalar(35) / Scalar(27),
		Scalar(0),

		Scalar(1631) / Scalar(55296),
		Scalar(175) / Scalar(512),
		Scalar(575) / Scalar(13824),
		Scalar(44275) / Scalar(110592),
		Scalar(253) / Scalar(4096)
	};
	static Scalar c4[6] = {
		Scalar(2825) / Scalar(27648),
		Scalar(0),
		Scalar(18575) / Scalar(48384),
		Scalar(13525) / Scalar(55296),
		Scalar(277) / Scalar(14336),
		Scalar(1) / Scalar(4),
	};
	static Scalar c5[6] = {
		Scalar(37) / Scalar(378),
		Scalar(0),
		Scalar(250) / Scalar(621),
		Scalar(125) / Scalar(594),
		Scalar(0),
		Scalar(512) / Scalar(1771)
	};

	CPPAD_ASSERT_KNOWN(
		M >= 1,
		"Error in Runge45: the number of steps is less than one"
	);
	CPPAD_ASSERT_KNOWN(
		e.size() == xi.size(),
		"Error in Runge45: size of e not equal to size of xi"
	);
	size_t i, j, k, m;              // indices

	size_t  n = xi.size();           // number of components in X(t)
	Scalar  ns = Scalar(int(M));     // number of steps as Scalar object
	Scalar  h = (tf - ti) / ns;      // step size
	Scalar  zero_or_nan = Scalar(0); // zero (nan if Ode returns has a nan)
	for(i = 0; i < n; i++)           // initialize e = 0
		e[i] = zero_or_nan;

	// vectors used to store values returned by F
	Vector fh(6 * n), xtmp(n), ftmp(n), x4(n), x5(n), xf(n);

	xf = xi;           // initialize solution
	for(m = 0; m < M; m++)
	{	// time at beginning of this interval
		// (convert to int to avoid MS compiler warning)
		Scalar t = ti * (Scalar(int(M - m)) / ns)
		         + tf * (Scalar(int(m)) / ns);

		// loop over integration steps
		x4 = x5 = xf;   // start x4 and x5 at same point for each step
		for(j = 0; j < 6; j++)
		{	// loop over function evaluations for this step
			xtmp = xf;  // location for next function evaluation
			for(k = 0; k < j; k++)
			{	// loop over previous function evaluations
				Scalar bjk = b[ (j-1) * 5 + k ];
				for(i = 0; i < n; i++)
				{	// loop over elements of x
					xtmp[i] += bjk * fh[i * 6 + k];
				}
			}
			// ftmp = F(t + a[j] * h, xtmp)
			F.Ode(t + a[j] * h, xtmp, ftmp);

			// if ftmp has a nan, set zero_or_nan to nan
			for(i = 0; i < n; i++)
				zero_or_nan *= ftmp[i];

			for(i = 0; i < n; i++)
			{	// loop over elements of x
				Scalar fhi    = ftmp[i] * h;
				fh[i * 6 + j] = fhi;
				x4[i]        += c4[j] * fhi;
				x5[i]        += c5[j] * fhi;
				x5[i]        += zero_or_nan;
			}
		}
		// accumulate error bound
		for(i = 0; i < n; i++)
		{	// cant use abs because cppad.hpp may not be included
			Scalar diff = x5[i] - x4[i];
			e[i] += fabs(diff);
			e[i] += zero_or_nan;
		}

		// advance xf for this step using x5
		xf = x5;
	}
	return xf;
}
Beispiel #8
0
/**
 * Description not yet available.
 * \param
 */
void laplace_approximation_calculator::
  do_separable_stuff_laplace_approximation_importance_sampling_adjoint
  (df1b2variable& ff)
{
  num_separable_calls++;
  set_dependent_variable(ff);
  //df1b2_gradlist::set_no_derivatives();
  df1b2variable::passnumber=1;
  df1b2_gradcalc1();

  init_df1b2vector & locy= *funnel_init_var::py;
  imatrix& list=*funnel_init_var::plist;

  int us=0; int xs=0;
#ifndef OPT_LIB
  assert(funnel_init_var::num_active_parameters <= INT_MAX);
#endif
  ivector lre_index(1,(int)funnel_init_var::num_active_parameters);
  ivector lfe_index(1,(int)funnel_init_var::num_active_parameters);

  for (int i=1;i<=(int)funnel_init_var::num_active_parameters;i++)
  {
    if (list(i,1)>xsize)
    {
      lre_index(++us)=i;
    }
    else if (list(i,1)>0)
    {
      lfe_index(++xs)=i;
    }
  }

  dvector local_xadjoint(1,xs);

  if (us>0)
  {
    dmatrix local_Hess(1,us,1,us);
    dvector local_grad(1,us);
    dmatrix local_Dux(1,us,1,xs);
    local_Hess.initialize();
    dvector local_uadjoint(1,us);
    for (int i=1;i<=us;i++)
    {
      for (int j=1;j<=us;j++)
      {
        int i2=list(lre_index(i),2);
        int j2=list(lre_index(j),2);
        local_Hess(i,j)+=locy(i2).u_bar[j2-1];
      }
    }

    for (int i=1;i<=us;i++)
    {
      for (int j=1;j<=xs;j++)
      {
        int i2=list(lre_index(i),2);
        int j2=list(lfe_index(j),2);
        local_Dux(i,j)=locy(i2).u_bar[j2-1];
      }
    }

    double f=0.0;
    initial_df1b2params::cobjfun+=f;

    for (int i=1;i<=us;i++)
    {
      for (int j=1;j<=us;j++)
      {
        int i2=list(lre_index(i),2);
        int j2=list(lre_index(j),2);
        locy(i2).get_u_bar_tilde()[j2-1]=
          (*block_diagonal_vhessianadjoint)(num_separable_calls)(i,j);
      }
    }

    df1b2variable::passnumber=2;
    df1b2_gradcalc1();

    df1b2variable::passnumber=3;
    df1b2_gradcalc1();
    dvector xtmp(1,xs);
    xtmp.initialize();

    local_uadjoint.initialize();
    local_xadjoint.initialize();
    for (int i=1;i<=xs;i++)
    {
      int i2=list(lfe_index(i),2);
      xtmp(i)+=locy[i2].u_tilde[0];
      local_xadjoint(i)+=locy[i2].u_tilde[0];
    }
    dvector utmp(1,us);
    utmp.initialize();
    for (int i=1;i<=us;i++)
    {
      int i2=list(lre_index(i),2);
      utmp(i)+=locy[i2].u_tilde[0];
      local_uadjoint(i)+=locy[i2].u_tilde[0];
    }
    if (xs>0)
      local_xadjoint -= solve(local_Hess,local_uadjoint)*local_Dux;
  }
  for (int i=1;i<=xs;i++)
  {
    int ii=lfe_index(i);
    check_local_xadjoint2(list(ii,1))+=local_xadjoint(i);
  }
  f1b2gradlist->reset();
  f1b2gradlist->list.initialize();
  f1b2gradlist->list2.initialize();
  f1b2gradlist->list3.initialize();
  f1b2gradlist->nlist.initialize();
  f1b2gradlist->nlist2.initialize();
  f1b2gradlist->nlist3.initialize();
  funnel_init_var::num_vars=0;
  funnel_init_var::num_active_parameters=0;
  funnel_init_var::num_inactive_vars=0;
}
/*!
 * \brief
 * Search
 * 
 * \param x
 * Initial point.
 * 
 * \returns
 * Final finess value.
 *  
 */
double LocalSearch_ES::search(vector<double>& x) 
{
	unsigned evalCount = 0;

	vector<double> xtmp(x.size()), xbest = x;
	double fx = evaluate(x), fbest = fx, ftmp;
	int nGood = 0, nBad = 0, restart = 0;
	while(evalCount < evaluationLimit)
	{
		if (sigma < 1e-5) 
		{ 
			sigma = 1.0 / sqrt(1.0 * fObj->nDimensions()); 
			restart++; 
			if (restart > 3) break;
		}
		
		// each iteration we generate lambda offspring
		// every n offspring (n = number of dimension), we check the adaptation rule
		for(unsigned i=0; i<fObj->nDimensions(); i++)
		{
			xtmp[i] = xbest[i] + Rng::gauss() * sigma;
		}		
		ftmp = evaluate(xtmp);
		evalCount++;

		// checking if the new point is better than the current point
		if (ftmp <= fx)
		{
			nGood++;			
		}
		else
		{
			nBad++;
		}

		// store the best point
		if (ftmp < fbest) 
		{ 
			xbest = xtmp;
			fbest = ftmp;
		}

		// 1/5 adaptive rule
		if (nGood + nBad == fObj->nDimensions())
		{
			if (nGood >= 0.2 * fObj->nDimensions()) sigma/=0.85; else sigma*=0.85;
			nGood = nBad = 0;
		}

		// every lambda points, we should update the x once
		if (evalCount % lambda == 0 || evalCount == evaluationLimit || fbest < this->accuracy)
		{
			if (fbest < fx)
			{
				x = xbest;
				fx = fbest;
			}

			if (fx < this->accuracy) break;
		}
	}
	
	return fx;
}
	real PseudoBoolean<real>::minimize_reduction_M(vector<label>& x, int& nlabelled) const
	{
		//
		// Compute a large enough value for M
		//
		real M = 0;
		for (auto itr=aijkl.begin(); itr != aijkl.end(); ++itr) {
			real a = itr->second;
			M += abs(a);
		}
		for (auto itr=aijk.begin(); itr != aijk.end(); ++itr) {
			real a = itr->second;
			M += abs(a);
		}
		for (auto itr=aij.begin(); itr != aij.end(); ++itr) {
			real a = itr->second;
			M += abs(a);
		}
		for (auto itr=ai.begin(); itr != ai.end(); ++itr) {
			real a = itr->second;
			M += abs(a);
		} 

		// Copy of the polynomial. Will contain the reduced polynomial
		PseudoBoolean<real> pb = *this;
		// Number of variables (will be increased
		int n = nvars();
		int norg = n;

		//
		// Reduce the degree-4 terms 
		//
		double M2 = M;
		for (auto itr=pb.aijkl.begin(); itr != pb.aijkl.end(); ++itr) {
			int i = get_i(itr->first);
			int j = get_j(itr->first);
			int k = get_k(itr->first);
			int l = get_l(itr->first);
			real a = itr->second;

			// a*xi*xj*xk*xl  will be converted to
			// a*xi*xj*z + M( xk*xl - 2*xk*z - 2*xl*z + 3*z )
			int z = n++;

			pb.add_monomial(i,j,z, a);

			pb.add_monomial(k,l,M);
			pb.add_monomial(k,z, -2*M);
			pb.add_monomial(l,z, -2*M);
			pb.add_monomial(z, 3*M);

			M2 += a + 4*M;
		}
		
		// Remove all degree-4 terms.
		pb.aijkl.clear();

		//
		// Reduce the degree-3 terms
		//
		for (auto itr=pb.aijk.begin(); itr != pb.aijk.end(); ++itr) {
			int i = get_i(itr->first);
			int j = get_j(itr->first);
			int k = get_k(itr->first);
			real a = itr->second;

			// a*xi*xj*xk  will be converted to
			// a*xi*z + M( xj*xk -2*xj*z -2*xk*z + 3*z )
			int z = n++;

			pb.add_monomial(i,z, a);

			pb.add_monomial(j,k,M2);
			pb.add_monomial(j,z, -2*M2);
			pb.add_monomial(k,z, -2*M2);
			pb.add_monomial(z, 3*M2);
		}

		// Remove all degree-3 terms.
		pb.aijk.clear();

		//
		// Minimize the reduced polynomial
		//
		vector<label> xtmp(n,-1);
		real bound = pb.minimize(xtmp, HOCR);

		nlabelled = 0;
		for (int i=0;i<norg;++i) {
			x.at(i) = xtmp[i];
			if (x[i] >= 0) {
				nlabelled++;
			}
		}

		return bound;
	}