cplexopldocplex

External calculation within opl


I'm implementing a CP model with cplex but within the steps I want some data to be calculated in a function or some similar form then resolve the model using the result.

What I need to calculate is the angle between two points with a third one as the center point e.g.

Input: x=(5, 0), y=(0, 0), z=(0, 5)

Output:

Angle = 90.0 degrees or 1.571 rad

Check this link for more details Or this link

In python the function would look like this:

def calculate_angle(
        first_point: Vector, center_point: Vector,
        second_point: Vector) -> float:
    """ Calculate the angel between three points. """
    # Change to scalars
    a = np.array(first_point.as_list())
    b = np.array(center_point.as_list())
    c = np.array(second_point.as_list())

    # Calculate vector differences
    ba = a - b
    bc = c - b

    # Find the angle in degrees
    cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
    cosine_angle = np.clip(cosine_angle, -1, 1)
    angle_radians = np.arccos(cosine_angle)
    return angle_radians

Is there a way to use the arccos in opl? or call the python function in the model file?

Thanks!


Solution

  • In OPL scripting you can call any function in java as can be seen in this example: https://github.com/AlexFleischerParis/oplscripting/blob/main/IloOplCallJava.mod

    execute
    {
      
    var convert180toRadian = IloOplCallJava("java.lang.Math","toRadians", "", 180);
    writeln("180 degrees in radian : ",convert180toRadian);
                    
    }   
    
    /*
    180 degrees in radian : 3.141592654
    */  
    
    execute
    {
    var title="title";
    var msg="hello";  
    
    IloOplCallJava("javax.swing.JOptionPane", "showMessageDialog",
    "(Ljava/awt/Component;Ljava/lang/Object;Ljava/lang/String;I)V", 
    null, msg, title, 1);    
    }  
    
    execute{ 
    writeln("5 random numbers");
    var rnd = IloOplCallJava("java.util.Random", "<init>", "()");
    rnd.setSeed(1); 
    for(var i=1;i<=5;i++)
    {                             
     var t = rnd.nextDouble();
     writeln(t);
    }
    writeln("Gaussian");
    for(var i=1;i<=5;i++)
    {                             
     var t = rnd.nextGaussian();
     writeln(t);
    }
    }
    

    If you want to call your python code you can use IloOplExec in scripting as can be seen at https://github.com/AlexFleischerParis/opltipsandtricks/blob/master/barchart.mod

    {string} options={"OPL" , "Python", "Java", "C++/C/C#", "Julia", "Other"};
    float percent[options]=[32, 25, 16,  14, 4, 9];
    string lastOption=last(options);
    
    execute
    {
    
    var o=new IloOplOutputFile("c:\\temp\\parambarchart.txt");
    o.writeln("Which API do you use when you call CPLEX ?");
    o.writeln("CPLEX API");
    o.writeln("% of use");
    
    for(var i in options) 
    {
     o.write(i);
     if (i!=lastOption) o.write(",");
    }
    o.writeln();
    
    for(var i in options) 
    {
     o.write(percent[i]);
     if (i!=lastOption) o.write(",");
    }
    o.writeln();
    o.close();
    
    IloOplExec("C:\\Python36\\python.exe c:\\temp\\displaybarchart.py",false);
    
    }
    

    and in displaybarchart.py

    import matplotlib.pyplot as plt
    
    plt.style.use('ggplot')
    
    f = open("c://temp//parambarchart.txt", "r")
    title=f.readline()
    xlabel=f.readline()
    ylabel=f.readline()
    xstring=f.readline()
    ystring=f.readline()
    f.close();
    
    x =  xstring.split(",")
    values = [float(i) for i in ystring.split(",")]
    
    x_pos = [i for i, _ in enumerate(x)]
    
    plt.bar(x_pos, values, color='green')
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    plt.title(title)
    
    plt.xticks(x_pos, x)
    
    plt.show()