////
//  Copyright: Copyright (c) MOSEK ApS, Denmark. All rights reserved.
//
//  File:      facility_location.java
//
//  Purpose: Demonstrates a small one-facility location problem.
//
//  Given 10 customers placed in a grid we wish to place a facility
//  somewhere so that the total sum of distances to customers is
//  minimized.
//
//  The problem is formulated as a conic optimization problem as follows.
//  Let f=(fx,fy) be the (unknown) location of the facility, and let
//  c_i=(cx_i,cy_i) be the (known) customer locations; then we wish to
//  minimize
//      sum_i || f - c_i ||
//  where
//      ||.||
//  denotes the euclidian norm.
//  This is formulated as
//
//  minimize   sum(d_i)
//  such that  d_i ^ 2 > tx_i ^ 2 + ty_i ^ 2, for all i
//             tx_i = cx_i - fx, for all i
//             ty_i = cy_i - fy, for all i
//             d_i > 0, for all i
////
package com.mosek.fusion.examples;
import mosek.fusion.*;

public class facility_location {
  // Customer locations
  //TAG:begin-data
  private static Matrix
  customerloc = Matrix.dense( new double[][] {
  {12.,  2. },
  {15., 13. },
  {10.,  8. },
  { 0., 10. },
  { 6., 13. },
  { 5.,  8. },
  {10., 12. },
  { 4.,  6. },
  { 5.,  2. },
  { 1., 10. } } );

  static int N = customerloc.numRows();
  //TAG:end-data
  public static void main(String[] args)
  throws SolutionError {
    //TAG:begin-vars
    Model M = new Model("FacilityLocation");
    // Variable holding the facility location
    try {
      //TAG:begin-new-var
      Variable f = M.variable("facility", Set.make(1, 2), Domain.unbounded());
      //TAG:end-new-var
      // Variable defining the euclidian distances to each customer
      Variable d = M.variable("dist", Set.make(N, 1), Domain.greaterThan(0.0));
      // Variable defining the x and y differences to each customer;
      Variable t = M.variable("t", Set.make(N, 2), Domain.unbounded());
      //TAG:end-vars
      //TAG:begin-cones
      M.constraint("dist measure",
      Var.stack(new Variable[][] {{ d, t }}),
      Domain.inQCone(N, 3));
      //TAG:end-cones

      //TAG:begin-xy-diff
      Variable fxy = Var.repeat(f, N);
      //TAG:begin-new-con
      M.constraint("xy diff", Expr.add(t, fxy), Domain.equalsTo(customerloc));
      //TAG:end-new-con
      //TAG:end-xy-diff

      //TAG:begin-objective
      M.objective("total_dist", ObjectiveSense.Minimize, Expr.sum(d));
      //TAG:end-objective

      //TAG:begin-get-result
      M.solve();

      double[] res = f.level();
      System.out.println("Facility location = " + res[0] + "," + res[1]);
      //TAG:end-get-result
    } finally {
      M.dispose();
    }
  }
}
