NAG Library Manual, Mark 30.2
Interfaces:  FL   CL   CPP   AD 

NAG CL Interface Introduction
Example description
/* nag_opt_handle_set_quadmatineq (e04rpc) Example Program.
 *
 * Copyright 2024 Numerical Algorithms Group.
 *
 * Mark 30.2, 2024.
 */

/* Read a 'generic' LMI/BMI SDP problem from the input file,
 * formulate the problem via a handle and pass it to the solver.
 */
#include <nag.h>
#include <stdio.h>

int main(void) {
  Integer exit_status = 0;
  Integer blkidx, dimaq, idblk, idlc, idx, idxend, inform, j, midx, nblk, nclin,
      nnzasum, nnzb, nnzc, nnzh, nnzqsum, nq, nvar;
  double *a = 0, *b = 0, *bl = 0, *bu = 0, *cvec = 0, *h = 0, *q = 0, *x = 0;
  double rinfo[32], stats[32];
  Integer *icola = 0, *icolb = 0, *icolh = 0, *icolq = 0, *idxc = 0, *irowa = 0,
          *irowb = 0, *irowh = 0, *irowq = 0, *nnza = 0, *nnzq = 0, *qi = 0,
          *qj = 0;
  void *handle = 0;
  /* Nag Types */
  NagError fail;

  printf("nag_opt_handle_set_quadmatineq (e04rpc) Example Program Results\n\n");
  fflush(stdout);

  /* Skip heading in the data file. */
  scanf("%*[^\n]");

  /* Read the problem size. */
  scanf("%" NAG_IFMT "%*[^\n]", &nvar);
  scanf("%" NAG_IFMT "%*[^\n]", &nnzh);
  scanf("%" NAG_IFMT " %" NAG_IFMT "%*[^\n]", &nclin, &nnzb);
  scanf("%" NAG_IFMT "%*[^\n]", &nblk);

  /* nag_opt_handle_init (e04rac).
   * Initialize an empty problem handle with nvar variables. */
  nag_opt_handle_init(&handle, nvar, NAGERR_DEFAULT);

  /* Read the linear part of the objective function. */
  if (!(cvec = NAG_ALLOC(nvar, double))) {
    printf("Allocation failure\n");
    exit_status = -1;
    goto END;
  }
  for (j = 0; j < nvar; j++)
    scanf("%lf", &cvec[j]);
  scanf("%*[^\n]");

  if (nnzh == 0) {
    /* nag_opt_handle_set_linobj (e04rec).
     * Add the linear objective function to the formulation. */
    nag_opt_handle_set_linobj(handle, nvar, cvec, NAGERR_DEFAULT);
    NAG_FREE(cvec);
  } else {
    /* The objective function is quadratic. Read nonzeros for H. */
    if (!(irowh = NAG_ALLOC(nnzh, Integer)) ||
        !(icolh = NAG_ALLOC(nnzh, Integer)) || !(h = NAG_ALLOC(nnzh, double)) ||
        !(idxc = NAG_ALLOC(nvar, Integer))) {
      printf("Allocation failure\n");
      exit_status = -1;
      goto END;
    }

    for (idx = 0; idx < nnzh; idx++)
      scanf("%lf %" NAG_IFMT " %" NAG_IFMT "%*[^\n]", &h[idx], &irowh[idx],
            &icolh[idx]);

    /* The linear part of the objective was read in as dense,
     * but the sparse format is needed here. */
    for (idx = 0; idx < nvar; idx++)
      idxc[idx] = idx + 1;
    nnzc = nvar;

    /* nag_opt_handle_set_quadobj (e04rfc).
     * Add the quadratic objective to the handle.*/
    nag_opt_handle_set_quadobj(handle, nnzc, idxc, cvec, nnzh, icolh, irowh, h,
                               NAGERR_DEFAULT);
    NAG_FREE(idxc);
    NAG_FREE(cvec);
    NAG_FREE(irowh);
    NAG_FREE(icolh);
    NAG_FREE(h);
  }

  /* Read a block of linear constraints and its bounds if present. */
  if (nclin > 0 && nnzb > 0) {
    if (!(irowb = NAG_ALLOC(nnzb, Integer)) ||
        !(icolb = NAG_ALLOC(nnzb, Integer)) || !(b = NAG_ALLOC(nnzb, double)) ||
        !(bl = NAG_ALLOC(nclin, double)) || !(bu = NAG_ALLOC(nclin, double))) {
      printf("Allocation failure\n");
      exit_status = -1;
      goto END;
    }

    /* Read all matrix B nonzeros. */
    for (idx = 0; idx < nnzb; idx++)
      scanf("%lf %" NAG_IFMT " %" NAG_IFMT "%*[^\n]", &b[idx], &irowb[idx],
            &icolb[idx]);

    /* Read the lower and upper bounds. */
    for (j = 0; j < nclin; j++)
      scanf("%lf", &bl[j]);
    scanf("%*[^\n]");
    for (j = 0; j < nclin; j++)
      scanf("%lf", &bu[j]);
    scanf("%*[^\n]");
    idlc = 0;

    /* nag_opt_handle_set_linconstr (e04rjc).
     * Add the block of linear constraints to the problem formulation. */
    nag_opt_handle_set_linconstr(handle, nclin, bl, bu, nnzb, irowb, icolb, b,
                                 &idlc, NAGERR_DEFAULT);
    NAG_FREE(irowb);
    NAG_FREE(icolb);
    NAG_FREE(b);
    NAG_FREE(bl);
    NAG_FREE(bu);
  }

  /* Read all matrix inequalities. */
  for (blkidx = 0; blkidx < nblk; blkidx++) {
    /* Read the dimension of this matrix inequality. */
    scanf("%" NAG_IFMT "%*[^\n]", &dimaq);
    /* Read the number of all nonzeros in linear and bilinear parts. */
    scanf("%" NAG_IFMT " %" NAG_IFMT "%*[^\n]", &nnzasum, &nnzqsum);
    idblk = 0;

    if (nnzasum > 0) {
      /* Read a linear matrix inequality composed of (nvar+1) matrices. */
      if (!(nnza = NAG_ALLOC(nvar + 1, Integer)) ||
          !(irowa = NAG_ALLOC(nnzasum, Integer)) ||
          !(icola = NAG_ALLOC(nnzasum, Integer)) ||
          !(a = NAG_ALLOC(nnzasum, double))) {
        printf("Allocation failure\n");
        exit_status = -1;
        goto END;
      }

      idx = 0;
      for (midx = 0; midx <= nvar; midx++) {
        /* Read matrix A_midx. */
        scanf("%" NAG_IFMT "%*[^\n]", &nnza[midx]);
        idxend = idx + nnza[midx];
        for (; idx < idxend; idx++)
          scanf("%lf %" NAG_IFMT " %" NAG_IFMT "%*[^\n]", &a[idx], &irowa[idx],
                &icola[idx]);
      }
      idblk = 0;

      /* nag_opt_handle_set_linmatineq (e04rnc).
       * Add the linear matrix constraint to the problem formulation. */
      nag_opt_handle_set_linmatineq(handle, nvar, dimaq, nnza, nnzasum, irowa,
                                    icola, a, 1, NULL, &idblk, NAGERR_DEFAULT);

      NAG_FREE(nnza);
      NAG_FREE(irowa);
      NAG_FREE(icola);
      NAG_FREE(a);
    }

    if (nnzqsum > 0) {
      /* Read bilinear part of the matrix inequality composed
       * of nq matrices. */
      scanf("%" NAG_IFMT "%*[^\n]", &nq);
      if (!(qi = NAG_ALLOC(nq, Integer)) || !(qj = NAG_ALLOC(nq, Integer)) ||
          !(nnzq = NAG_ALLOC(nq, Integer)) ||
          !(irowq = NAG_ALLOC(nnzqsum, Integer)) ||
          !(icolq = NAG_ALLOC(nnzqsum, Integer)) ||
          !(q = NAG_ALLOC(nnzqsum, double))) {
        printf("Allocation failure\n");
        exit_status = -1;
        goto END;
      }

      idx = 0;
      for (midx = 0; midx < nq; midx++) {
        /* Read matrix Q_ij where i=qi[midx], j=qj[midx]. */
        scanf("%" NAG_IFMT " %" NAG_IFMT "%*[^\n]", &qi[midx], &qj[midx]);
        scanf("%" NAG_IFMT "%*[^\n]", &nnzq[midx]);
        idxend = idx + nnzq[midx];
        for (; idx < idxend; idx++)
          scanf("%lf %" NAG_IFMT " %" NAG_IFMT "%*[^\n]", &q[idx], &irowq[idx],
                &icolq[idx]);
      }

      /* nag_opt_handle_set_quadmatineq (e04rpc).
       * Expand the existing linear matrix inequality with the bilinear
       * terms or (if the linear part was not present) create a
       * new matrix inequality. */
      nag_opt_handle_set_quadmatineq(handle, nq, qi, qj, dimaq, nnzq, nnzqsum,
                                     irowq, icolq, q, &idblk, NAGERR_DEFAULT);

      NAG_FREE(qi);
      NAG_FREE(qj);
      NAG_FREE(nnzq);
      NAG_FREE(irowq);
      NAG_FREE(icolq);
      NAG_FREE(q);
    }
  }

  /* Problem was successfully decoded. */
  printf("SDP problem was read, passing it to the solver.\n\n");
  fflush(stdout);

  /* nag_opt_handle_print (e04ryc).
   * Print overview of the handle. */
  nag_opt_handle_print(handle, 6, "Overview, Matrix Constraints",
                       NAGERR_DEFAULT);

  /* Allocate memory for the solver. */
  if (!(x = NAG_ALLOC(nvar, double))) {
    printf("Allocation failure\n");
    exit_status = -1;
    goto END;
  }
  for (j = 0; j < nvar; j++)
    x[j] = 0.0;

  /* Call the solver nag_opt_handle_solve_pennon (e04svc),
   * ignore Lagrangian multipliers. */
  INIT_FAIL(fail);
  nag_opt_handle_solve_pennon(handle, nvar, x, 0, NULL, 0, NULL, 0, NULL, rinfo,
                              stats, &inform, &fail);
  if (fail.code != NE_NOERROR) {
    printf("Error from nag_opt_handle_solve_pennon (e04svc).\n%s\n",
           fail.message);
    exit_status = 1;
    goto END;
  }

END:

  /* nag_opt_handle_free (e04rzc).
   * Destroy the problem handle and deallocate all the memory. */
  if (handle)
    nag_opt_handle_free(&handle, NAGERR_DEFAULT);

  NAG_FREE(x);

  return exit_status;
}