Quintiq file version 2.0 
 | 
#parent: #root 
 | 
Method PositionNodes 
 | 
{ 
 | 
  Description: 'Position the nodes on the X axis.' 
 | 
  TextBody: 
 | 
  [* 
 | 
    program := LibOpt_Utility::ConstructMathematicalProgramWithoutAnalysis(); 
 | 
     
 | 
    nodes := selectset( this, UIGraphRow.UIGraphNode, node, true ); 
 | 
     
 | 
    // In this algorithm we use abs dist parts. 
 | 
    // To calculate |x| (the absolute value of x) we do create 2 new variables x_pos and x_neg 
 | 
    // Then we add the following constraints: 
 | 
    // x = x_pos - x_neg 
 | 
    // x_pos, x_neg >= 0 
 | 
    // Either x_pos = x and x_neg = 0, or x_neg = x and x_pos = 0. 
 | 
    // With this, we know that |x| = x_pos + x_neg 
 | 
     
 | 
    // We use |x| for the distance to the average parent and the average child. 
 | 
    // The average parent is the average of the position of all its parents. 
 | 
    // Similarly, the average child is the average of the positions of all its children. 
 | 
     
 | 
    abs_dist_parts := construct( Strings ); 
 | 
    abs_dist_parts.Add( LibOpt_UIGraph::VarDistParents( true ) ); 
 | 
    abs_dist_parts.Add( LibOpt_UIGraph::VarDistParents( false ) ); 
 | 
    abs_dist_parts.Add( LibOpt_UIGraph::VarDistChildren( true ) ); 
 | 
    abs_dist_parts.Add( LibOpt_UIGraph::VarDistChildren( false ) ); 
 | 
     
 | 
    traverse( nodes, Elements, node ) 
 | 
    { 
 | 
      var := program.NewContinuousVariable( LibOpt_UIGraph::VarX(), node ); 
 | 
      var.LowerBound( node.Width() / 2 ); 
 | 
      var.UpperBound( Real::MaxReal() ); 
 | 
       
 | 
      traverse( abs_dist_parts, Elements, abs_dist_part ) 
 | 
      { 
 | 
        var_abs_dist_part := program.NewContinuousVariable( abs_dist_part, node ); 
 | 
        var_abs_dist_part.LowerBound( 0.0 ); 
 | 
        var_abs_dist_part.UpperBound( Real::MaxReal() ); 
 | 
      } 
 | 
    } 
 | 
     
 | 
    // Constraint on order of nodes, making sure nodes do not overlap. 
 | 
    traverse( nodes, Elements, node, not isnull( node.Previous() ) ) 
 | 
    { 
 | 
      var_prev := program.Variable( LibOpt_UIGraph::VarX(), node.Previous() ); 
 | 
      var := program.Variable( LibOpt_UIGraph::VarX(), node ); 
 | 
      constr := program.NewConstraint( LibOpt_UIGraph::ConstrChain(), node ); 
 | 
      constr.NewTerm( 1.0, var ); 
 | 
      constr.NewTerm( -1.0, var_prev ); 
 | 
      constr.Sense( MPConstraintSense::GreaterOrEqual().AsString() ); 
 | 
      constr.RHSValue( node.Previous().Width() / 2 + node.Width() / 2 + this.Offset() ); 
 | 
    } 
 | 
     
 | 
    // Calculate the abs( dist ) w.r.t. the average parent 
 | 
    traverse( nodes, Elements, node ) 
 | 
    { 
 | 
      parents := selectset( node, Incoming.Origin, n, not isnull( n.UIGraphRow() ) ); 
 | 
      LibOpt_UIGraph::NewAbsConstraint( program, node, parents, 
 | 
                                        LibOpt_UIGraph::ConstrCalcDistParents(), 
 | 
                                        LibOpt_UIGraph::VarDistParents( true ), 
 | 
                                        LibOpt_UIGraph::VarDistParents( false ), 
 | 
                                        average( node, Incoming, arc, not isnull( arc.Origin().UIGraphRow() ), arc.First().X() ) ); 
 | 
    } 
 | 
     
 | 
    // Calculate the abs( dist ) w.r.t. the average child 
 | 
    traverse( nodes, Elements, node ) 
 | 
    { 
 | 
      children := selectset( node, Outgoing.Destination, n, not isnull( n.UIGraphRow() ) ); 
 | 
      LibOpt_UIGraph::NewAbsConstraint( program, node, children, 
 | 
                                        LibOpt_UIGraph::ConstrCalcDistChildren(), 
 | 
                                        LibOpt_UIGraph::VarDistChildren( true ), 
 | 
                                        LibOpt_UIGraph::VarDistChildren( false ), 
 | 
                                        average( node, Outgoing, arc, not isnull( arc.Destination().UIGraphRow() ), arc.Last().X() ) ); 
 | 
    } 
 | 
     
 | 
    // Goal 
 | 
    goal := program.Goal(); 
 | 
    traverse( nodes, Elements, node ) 
 | 
    { 
 | 
      goal.NewTerm( 1.0, program.Variable( LibOpt_UIGraph::VarDistParents( true ), node ) ); 
 | 
      goal.NewTerm( 1.0, program.Variable( LibOpt_UIGraph::VarDistParents( false ), node ) ); 
 | 
      goal.NewTerm( 1.0, program.Variable( LibOpt_UIGraph::VarDistChildren( true ), node ) ); 
 | 
      goal.NewTerm( 1.0, program.Variable( LibOpt_UIGraph::VarDistChildren( false ), node ) ); 
 | 
    } 
 | 
     
 | 
    program.Execute(); 
 | 
     
 | 
    { 
 | 
      constr := program.NewConstraint( LibOpt_UIGraph::ConstrGoal() ); 
 | 
      constr.Sense( MPConstraintSense::LessOrEqual().AsString() ); 
 | 
      constr.RHSValue( program.GoalValue() ); 
 | 
      traverse( goal.Terms(), Elements, term ) 
 | 
      { 
 | 
        constr.NewTerm( term.Coefficient(), term.Variable() ); 
 | 
        term.Coefficient( 0.0 ); 
 | 
      } 
 | 
       
 | 
      width := program.NewContinuousVariable( LibOpt_UIGraph::VarWidth() ); 
 | 
      traverse( this, UIGraphRow, row, row.UIGraphNode( relsize ) > 0 ) 
 | 
      { 
 | 
        constr := program.NewConstraint( LibOpt_UIGraph::ConstrMaxWidth(), row ); 
 | 
        constr.NewTerm( 1.0, width ); 
 | 
        constr.NewTerm( -1.0, program.Variable( LibOpt_UIGraph::VarX(), row.Last() ) ); 
 | 
        constr.Sense( MPConstraintSense::GreaterOrEqual().AsString() ); 
 | 
        constr.RHSValue( 0.0 ); 
 | 
      } 
 | 
       
 | 
      program.Goal().NewTerm( 1.0, width ); 
 | 
       
 | 
      if( not program.Execute() ) 
 | 
      { 
 | 
        epsilon := 0.0001; 
 | 
        constr.RHSValue( constr.RHSValue() + epsilon ); 
 | 
        program.Execute(); 
 | 
      } 
 | 
    } 
 | 
    // Handle result 
 | 
    traverse( nodes, Elements, node ) 
 | 
    { 
 | 
      var := program.Variable( LibOpt_UIGraph::VarX(), node ); 
 | 
      node.X( var.OptimalValue() ); 
 | 
    } 
 | 
  *] 
 | 
  InterfaceProperties { Accessibility: 'Module' } 
 | 
} 
 |