From 558b590810bcc8cb3c8056ccb4de1cc1769cfd3c Mon Sep 17 00:00:00 2001 From: Riccardo Elitropi Date: Wed, 23 Apr 2025 10:09:41 +0200 Subject: [PATCH] EgtNumKernel : - Aggiunta classe di calcolo per ottimizzazione ordine delle lavorazioni - Aggiunti vincoli obbligatori e dipendenze suggerite a ShortestPath. --- Dependences.cpp | 279 +++++++++++++++ EgtNumKernel.vcxproj | 3 + EgtNumKernel.vcxproj.filters | 12 + Fn.cpp | 79 +++-- Hybrid.cpp | 157 +++++++-- MachOptimization.cpp | 658 +++++++++++++++++++++++++++++++++++ MachOptimization.h | 73 ++++ Nn.cpp | 76 ++-- PntOpt.cpp | 60 +++- ShortestPath.cpp | 43 ++- ShortestPath.h | 33 ++ ShortestPathTsp.cpp | 138 +++++--- TwoOpt.cpp | 113 ++++-- 13 files changed, 1552 insertions(+), 172 deletions(-) create mode 100644 Dependences.cpp create mode 100644 MachOptimization.cpp create mode 100644 MachOptimization.h diff --git a/Dependences.cpp b/Dependences.cpp new file mode 100644 index 0000000..505dcbd --- /dev/null +++ b/Dependences.cpp @@ -0,0 +1,279 @@ +//---------------------------------------------------------------------------- +// EgalTech 2025-2025 +//---------------------------------------------------------------------------- +// File : Dependences.cpp Data : 16.04.25 Versione : 2.7a1 +// Contenuto : Scelta dei nodi in base alle dipenze per ShortestPath ; +// +// +// Modifiche : 16.04.25 RE Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "DllMain.h" +#include "ShortestPath.h" +#include "/EgtDev/Include/EGnStringUtils.h" +#include + +using namespace std ; + +//---------------------------------------------------------------------------- +/* Funzione per impostare i vincoli di precendeza obbligatori */ +bool +ShortestPath::SetConstraintOrder( int nPrev, int nNext) +{ + // se ho già delle dipendenze inserite, controllo che non sia già inserita o in conflitto + for ( auto it = m_vOrderConstr.begin() ; it != m_vOrderConstr.end() ; ++ it) { + // se dipendenza già presente, non la considero + if ( ( *it).first == nPrev && ( *it).second == nNext) { + LOG_INFO( GetENkLogger(), string( "Constraint Dependence already in : " + ToString( nPrev) + ", " + ToString( nNext)).c_str()) ; + return true ; + } + // se conflitto allora ritorno errore + if ( ( *it).first == nNext && ( *it).second == nPrev) { + LOG_INFO( GetENkLogger(), string( "Constraint Dependence conflict: " + ToString( nPrev) + ", " + ToString( nNext)).c_str()) ; + return false ; + } + } + // inserisco la dipendenze + m_vOrderConstr.emplace_back( make_pair( nPrev, nNext)) ; + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per impostare i vincoli di precedenza suggeriti */ +bool +ShortestPath::SetSuggestedOrder( int nPrev, int nNext) +{ + // se vettore dipendenze vuoto, lo alloco + if ( m_vOrderSugg.empty()) { + m_vOrderSugg.resize( m_nNumPnts * m_nNumPnts, false) ; + m_vOrderSuggRowMap.resize( m_nNumPnts, false) ; + } + // se in conflitto, allora non la inserisco e cancello quella presente + if ( m_vOrderSugg[ nNext * m_nNumPnts + nPrev]) { + m_vOrderSugg[ nNext * m_nNumPnts + nPrev] = false ; + return true ; + } + // inserisco la dipendenze + m_vOrderSugg[ nPrev * m_nNumPnts + nNext] = true ; + m_vOrderSuggRowMap[ nPrev] = true ; + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per verificare se un vincolo è violato o meno */ +bool +ShortestPath::IsViolatingContraints( int nPrev, int nNext) +{ + // scorro i vincoli + for ( const INTINT nC : m_vOrderConstr) { + // se il vincolo è ( nNext, nPrev) allora non è violato + if ( nC.first == nNext && nC.second == nPrev) + return true ; + } + return false ; +} + +//---------------------------------------------------------------------------- +/* Funzione per verificare se un Path rispetta tutti i vincoli obbligatori */ +bool +ShortestPath::IsPathRespectingContraints( NODE* pPath) +{ + // controllo validità del percorso + if ( pPath == nullptr) + return false ; + // controllo se il percorso ricavato, viola un vincolo + for ( unsigned nC = 0 ; nC < m_vOrderConstr.size() ; ++ nC) { + // scorro il percorso + NODE* pCurr = pPath ; + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { + if ( pCurr->nPos == m_vOrderConstr[nC].first) + break ; + if ( pCurr->nPos == m_vOrderConstr[nC].second) + return false ; + pCurr = pCurr->pNext ; + } + } + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per verificare se una rotazione ciclica di nodi viola dei vincoli */ +bool +ShortestPath::IsChangingStartPathRespectingConstraints( NODE* pPath, NODE* pFirst) +{ + // copio il percorso in quello per le dipendenze + CopyPath( pPath, m_pCheckDep) ; + NODE* pCurr = m_pCheckDep ; + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { + // mi muovo alla posizione succesisva nel percorso di test + pCurr = pCurr->pNext ; + // controllo se il nuovo percorso rispetta i vincoli + if ( IsPathRespectingContraints( pCurr)) { + pFirst = pFirst->pNext ; + return true ; + } + } + return false ; +} + +//---------------------------------------------------------------------------- +/* Funzione per calcolare il numero di dipendenze SUGGERITE non rispettate da un Path */ +bool +ShortestPath::IsPathRespectingSuggestedDependences( NODE* pPath, int& nBreak) +{ + // se non ho dipendenze suggerite, ho finito + if ( m_vOrderSugg.empty()) + return true ; + // controllo validità del percorso + nBreak = 0 ; + if ( pPath == nullptr) + return false ; + // scrorro i nodi coinvolti da dipendenze + unsigned nDim = m_nNumPnts - ( m_nType == SP_OPEN ? 1 : 0) ; + for ( unsigned i = 0 ; i < nDim ; ++ i) { + // controllo se il nodo corrente è legato da dipendenze suggerite + if ( m_vOrderSuggRowMap[i]) { + // scorro il percorso + NODE* pCurr = pPath ; + for ( unsigned j = 0 ; j < m_nNumPnts ; ++ j) { + if ( pCurr->nPos != m_nNumPnts - 1) { + if ( pCurr->nPos == i) + break ; + if ( m_vOrderSugg[ i * nDim + pCurr->nPos]) + ++ nBreak ; + } + pCurr = pCurr->pNext ; + } + } + } + return ( nBreak == 0) ; +} + +//---------------------------------------------------------------------------- +/* Funzione per calcolare il coefficiente di costo per ogni dipedenza suggerita violata */ +unsigned +ShortestPath::CalcMaxDist( void) +{ + // scorro gli elementi della matrice delle distanze + // NB. Si può decidere di calcolarlo diversamente + if ( m_Dists == nullptr) + return false ; + unsigned nMaxDist = 0 ; + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { + for ( unsigned j = 0 ; j < m_nNumPnts ; ++ j) { + if ( i != j && m_Dists[ Index( i, j)] < MAXDIST - 1) + nMaxDist = max( nMaxDist, m_Dists[ Index( i, j)]) ; + } + } + return nMaxDist ; +} + +/* ------------------------------------------------------------------------- */ +bool +ShortestPath::ChooseFirstNode( unsigned& iFirst) +{ + // definisco un vettore dei soli nodi che posso inserire + vector vOkNodes ; vOkNodes.reserve( m_nNumPnts) ; + // scorro i nodi i-esimi + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { + // non deve esistere alcun vincolo del tipo ( j, i) + bool bOk = true ; + for ( unsigned nC = 0 ; nC < m_vOrderConstr.size() && bOk ; ++ nC) { + // recupero il vincolo ( a, b) + const INTINT& nnDep = m_vOrderConstr[nC] ; + bOk = nnDep.second != i ; + } + if ( bOk) + vOkNodes.push_back( i) ; + } + // se non ho nodi, allora errore + if ( vOkNodes.empty()) + return false ; + // scelgo l'ultimo ( per percorsi OPEN) + iFirst = vOkNodes.back() ; + return true ; +} + +/* ------------------------------------------------------------------------- */ +bool +ShortestPath::ChooseNextNode( NODE* pPath, unsigned nSize, unsigned& jNext, bool bNearVsFar) +{ + // recupero i nodi non inseriti nel path + jNext = -1 ; + vector vUninstertedNodes ; vUninstertedNodes.reserve( m_nNumPnts) ; + for ( unsigned nInd = 0 ; nInd < m_nNumPnts ; ++ nInd) { + bool bIn = false ; + NODE* pNode = pPath ; + for ( unsigned nNode = 0 ; nNode < nSize && ! bIn ; ++ nNode) { + bIn = ( pNode->nPos == nInd) ; + pNode = pNode->pPrev ; + } + if ( ! bIn) + vUninstertedNodes.push_back( nInd) ; + } + // se tutti i nodi sono inseriti, allora non faccio nulla + if ( vUninstertedNodes.empty()) + return true ; + // se un solo nodo non inserito, allora non ho altra scelta + if ( vUninstertedNodes.size() == unsigned( 1)) { + jNext = vUninstertedNodes[0] ; + return true ; + } + // definisco un vettore dei soli nodi che posso inserire + vector vOkNodes ; vOkNodes.reserve( m_nNumPnts) ; + // scorro i nodi che devo ancora posizionare + for ( unsigned j : vUninstertedNodes) { + /* + * l'ultimo nodo inserito è l'i-esimo, e il successivo sarà il j-esimo ( i ---> j) + * devo controllare che tra i nodi inseriti non ci sia un nodo k tale per cui viene violato + * il vincolo ( k, j) + */ + // controllo che l'arco ( i --> j) sia valido + if ( ! m_Available[ Index( pPath->nPos, j)]) + continue ; + // scorro i vincoli + bool bOk = true ; + for ( unsigned nC = 0 ; nC < m_vOrderConstr.size() && bOk ; ++ nC) { + // recupero il vincolo ( a, b) + const INTINT& nnDep = m_vOrderConstr[nC] ; + // controllo se esiste un vincolo del tipo ( ?, j) + if ( nnDep.second == j) { + // in questo caso controllo se esiste un nodo k != j che viola il vincolo ( k, j) + for ( unsigned k = 0 ; k < vUninstertedNodes.size() && bOk ; ++ k) { + if ( vUninstertedNodes[k] != j) + bOk = ( vUninstertedNodes[k] != nnDep.first) ; + } + } + } + // se vincolo non violato, allora il nodo j può essere inserito + if ( bOk) + vOkNodes.push_back( j) ; + } + // se non posso inserire alcun nodo, allora errore + if ( vOkNodes.empty()) + return false ; + // se un solo nodo possibile, allora è quello + if ( vOkNodes.size() == unsigned( 1)) { + jNext = vOkNodes[0] ; + return true ; + } + // altrimenti seleziono il nodo j-esimo tale per cui l'arco ( i --> j) ha costo minimo/assimo ( bNearVsFar) + int nRefDist = bNearVsFar ? MAXDIST : -1 ; + for ( unsigned j : vOkNodes) { + int nDist = int( ArcCost( pPath->nPos, j)) ; + bool bUpdate = false ; + if ( bNearVsFar) + bUpdate = ( nDist < nRefDist) ; + else + bUpdate = ( nDist > nRefDist) ; + if ( bUpdate) { + nRefDist = nDist ; + jNext = j ; + } + } + return true ; +} \ No newline at end of file diff --git a/EgtNumKernel.vcxproj b/EgtNumKernel.vcxproj index 8457128..ec18900 100644 --- a/EgtNumKernel.vcxproj +++ b/EgtNumKernel.vcxproj @@ -212,8 +212,10 @@ copy $(TargetPath) \EgtProg\Dll64 + + @@ -240,6 +242,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/EgtNumKernel.vcxproj.filters b/EgtNumKernel.vcxproj.filters index e4e0e4c..07d7e6e 100644 --- a/EgtNumKernel.vcxproj.filters +++ b/EgtNumKernel.vcxproj.filters @@ -25,6 +25,9 @@ {54a2fd6b-2491-42af-932a-eaa6f62f1ae2} + + {4413b322-852f-4def-8339-088d4420d41d} + @@ -72,6 +75,12 @@ File di origine\Dijkstra + + File di origine\MachOptimization + + + File di origine\ShortestPath + @@ -119,6 +128,9 @@ File di intestazione + + File di intestazione + diff --git a/Fn.cpp b/Fn.cpp index c7514c1..147a4a3 100644 --- a/Fn.cpp +++ b/Fn.cpp @@ -2,7 +2,7 @@ // EgalTech 2015-2015 //---------------------------------------------------------------------------- // File : NN.cpp Data : 24.12.15 Versione : 1.6l4 -// Contenuto : Calcolo del percorso minimo basandosi su punto più lontano ; +// Contenuto : Calcolo del percorso minimo basandosi su punto più lontano ; // pessima stima, utile come base per miglioramenti. // // @@ -15,7 +15,7 @@ #include "stdafx.h" #include "ShortestPath.h" -/*-------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ long long unsigned ShortestPath::FarNeighbor( void) { @@ -26,34 +26,59 @@ ShortestPath::FarNeighbor( void) m_Available[ Index( i, i)] = false ; } - // cerco il nodo con il più corto arco che se ne diparte - unsigned nCurr = 0 ; - unsigned nNew = HighArc( nCurr) ; - for ( unsigned i = 1 ; i < m_nNumPnts ; ++ i) { - unsigned j = HighArc( i) ; - if ( ArcCost( i, j) > ArcCost( nCurr, nNew)) { - nCurr = i ; - nNew = j ; + // se non ho dipendenze + if ( ! ExistConstraints()) { + // cerco il nodo con il più corto arco che se ne diparte + unsigned nCurr = 0 ; + unsigned nNew = HighArc( nCurr) ; + for ( unsigned i = 1 ; i < m_nNumPnts ; ++ i) { + unsigned j = HighArc( i) ; + if ( ArcCost( i, j) > ArcCost( nCurr, nNew)) { + nCurr = i ; + nNew = j ; + } } + // imposto il nodo corrente come base del percorso + NODE* pPath = m_pMain ; + pPath->nPos = nCurr ; + // ciclo + do { + // imposto tutti gli archi non disponibili al nodo corrente + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) + m_Available[ Index( i, nCurr)] = false ; + // aggiungo un nuovo nodo al percorso + pPath = pPath->pNext ; + pPath->nPos = nNew ; + // imposto il nuovo nodo come nodo corrente + nCurr = nNew ; + // cerco il nuovo nodo all'estremo dell'arco più corto dal nodo corrente + nNew = HighArc( nCurr) ; + } while ( m_Available[ Index( nCurr, nNew)]) ; } - - // imposto il nodo corrente come base del percorso - NODE* pPath = m_pMain ; - pPath->nPos = nCurr ; - - // ciclo - do { - // imposto tutti gli archi non disponibili al nodo corrente - for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) - m_Available[ Index( i, nCurr)] = false ; - // aggiungo un nuovo nodo al percorso - pPath = pPath->pNext ; + // se esistono dipendenze + else { + // cerco il primo nodo da cui iniziare + unsigned nNew = -1 ; + if ( ! ChooseFirstNode( nNew)) + return MAXDIST ; + // imposto il nodo corrente come base del percorso + NODE* pPath = m_pMain ; pPath->nPos = nNew ; - // imposto il nuovo nodo come nodo corrente - nCurr = nNew ; - // cerco il nuovo nodo all'estremo dell'arco più corto dal nodo corrente - nNew = HighArc( nCurr) ; - } while ( m_Available[ Index( nCurr, nNew)]) ; + unsigned nSize = 1 ; + // ciclo + do { + // imposto tutti gli archi non disponibili al nodo corrente + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) + m_Available[ Index( i, nNew)] = false ; + // cerco il nuovo nodo all'estremo dell'arco più lungo dal nodo corrente + if ( ! ChooseNextNode( pPath, nSize, nNew, false)) + return MAXDIST ; + // aggiungo un nuovo nodo al percorso + pPath = pPath->pNext ; + pPath->nPos = nNew ; + ++ nSize ; + } while ( nSize < m_nNumPnts) ; + } // calcolo il costo del percorso return TotalCost( m_pMain) ; diff --git a/Hybrid.cpp b/Hybrid.cpp index d37620b..1ee5f9d 100644 --- a/Hybrid.cpp +++ b/Hybrid.cpp @@ -17,6 +17,36 @@ #include "stdafx.h" #include "ShortestPath.h" +// ---------------------------------------------------------------------------- +static void +SwapNodesForHybrid1( NODE* pLast, NODE* pFirst, NODE* pKth) +{ + NODE* pSave = pKth->pNext ; + pKth->pNext = pKth->pNext->pNext ; + pKth->pNext->pPrev = pKth ; + pLast->pNext = pSave ; + pFirst->pPrev = pSave ; + pSave->pNext = pFirst ; + pSave->pPrev = pLast ; +} + +// ---------------------------------------------------------------------------- +static void +SwapNodesForHybrid2( NODE* pLast, NODE* pFirst, NODE* pKth) +{ + for ( NODE* pReverse = pFirst ; pReverse != pKth ;) { + NODE* pSave = pReverse->pNext ; + pReverse->pNext = pReverse->pPrev ; + pReverse->pPrev = pSave ; + pReverse = pSave ; + } + pFirst->pNext = pKth->pNext ; + pKth->pNext->pPrev = pFirst ; + pKth->pNext = pKth->pPrev ; + pKth->pPrev = pLast ; + pLast->pNext = pKth ; +} + // ---------------------------------------------------------------------------- long long unsigned ShortestPath::Hybrid( NODE* pPath) @@ -24,56 +54,109 @@ ShortestPath::Hybrid( NODE* pPath) const int MAX_TRY = 2048 ; unsigned count = 1 ; NODE* pFirst = pPath ; + NODE* pFirstDep = m_pCheckDep ; unsigned nTry = 1 ; do { NODE* pLast = pFirst->pPrev ; NODE* pKth = pFirst->pNext ; + NODE* pLastDep = pFirstDep->pPrev ; + NODE* pKthDep = pFirstDep->pNext ; do { - // Point-Opt ( D1=new, D3=original) - unsigned D1 = ArcCost( pKth->nPos, pKth->pNext->pNext->nPos) + - ArcCost( pKth->pNext->nPos, pFirst->nPos) + - ArcCost( pLast->nPos, pKth->pNext->nPos) ; - unsigned D3 = ArcCost( pLast->nPos, pFirst->nPos) + - ArcCost( pKth->nPos, pKth->pNext->nPos) + - ArcCost( pKth->pNext->nPos, pKth->pNext->pNext->nPos) ; - // Two-Opt ( D2=new, D4=original) - unsigned D2 = ArcCost( pFirst->nPos, pKth->pNext->nPos) + - ArcCost( pLast->nPos, pKth->nPos) ; - unsigned D4 = ArcCost( pLast->nPos, pFirst->nPos) + - ArcCost( pKth->nPos, pKth->pNext->nPos) ; - // Lunghezze originali e nuova del tratto intermedio che viene invertito - for ( NODE* pCurr = pFirst ; pCurr != pKth ; pCurr = pCurr->pNext) { - D4 += ArcCost( pCurr->nPos, pCurr->pNext->nPos) ; - D2 += ArcCost( pCurr->pNext->nPos, pCurr->nPos) ; + unsigned D1 = 0 ; + unsigned D2 = 0 ; + unsigned D3 = 0 ; + unsigned D4 = 0 ; + // se non esistono dipendenze suggerite + if ( ! ExistSuggDependences()) { + // Point-Opt ( D1=new, D3=original) + D1 = ArcCost( pKth->nPos, pKth->pNext->pNext->nPos) + + ArcCost( pKth->pNext->nPos, pFirst->nPos) + + ArcCost( pLast->nPos, pKth->pNext->nPos) ; + D3 = ArcCost( pLast->nPos, pFirst->nPos) + + ArcCost( pKth->nPos, pKth->pNext->nPos) + + ArcCost( pKth->pNext->nPos, pKth->pNext->pNext->nPos) ; + // Two-Opt ( D2=new, D4=original) + D2 = ArcCost( pFirst->nPos, pKth->pNext->nPos) + + ArcCost( pLast->nPos, pKth->nPos) ; + D4 = ArcCost( pLast->nPos, pFirst->nPos) + + ArcCost( pKth->nPos, pKth->pNext->nPos) ; + // Lunghezze originali e nuova del tratto intermedio che viene invertito + for ( NODE* pCurr = pFirst ; pCurr != pKth ; pCurr = pCurr->pNext) { + D4 += ArcCost( pCurr->nPos, pCurr->pNext->nPos) ; + D2 += ArcCost( pCurr->pNext->nPos, pCurr->nPos) ; + } } + // se esistono dipendenze suggerite + else { + D3 = static_cast( TotalCost( pPath)) ; + D4 = D3 ; + D1 = MAXDIST ; + D2 = MAXDIST ; + CopyPathAdv( pPath, m_pCheckDep, pLast, pFirst, pKth, &pLastDep, &pFirstDep, &pKthDep) ; + SwapNodesForHybrid1( pLastDep, pFirstDep, pKthDep) ; + bool bOkD1 = ( IsPathRespectingContraints( m_pCheckDep)) ; + if ( bOkD1) + D1 = static_cast( TotalCost( m_pCheckDep)) ; + CopyPathAdv( pPath, m_pCheckDep, pLast, pFirst, pKth, &pLastDep, &pFirstDep, &pKthDep) ; + SwapNodesForHybrid2( pLastDep, pFirstDep, pKthDep) ; + bool bOkD2 = ( IsPathRespectingContraints( m_pCheckDep)) ; + if ( bOkD2) + D2 = static_cast( TotalCost( m_pCheckDep)) ; + } + // controllo se ci sono miglioramenti nei persorsi if ( D1 < D3 || D2 < D4) { if ( D1 < D3 && ( D2 >= D4 || ( D3 - D1) >= ( D4 - D2))) { - NODE* pSave = pKth->pNext ; - pKth->pNext = pKth->pNext->pNext ; - pKth->pNext->pPrev = pKth ; - pLast->pNext = pSave ; - pFirst->pPrev = pSave ; - pSave->pNext = pFirst ; - pSave->pPrev = pLast ; + // nel caso di assenza di dipendenze + if ( ! ExistConstraints()) { + SwapNodesForHybrid1( pLast, pFirst, pKth) ; + count = 0 ; + pFirst = pLast->pNext ; + pKth = pFirst->pNext ; + } + // nel caso di dipendeze, controllo se il nuovo percorso le rispetta + else { + CopyPathAdv( pPath, m_pCheckDep, pLast, pFirst, pKth, &pLastDep, &pFirstDep, &pKthDep) ; + // effettuo i cambi dei nodi sul path di test per le dipendenze + SwapNodesForHybrid1( pLastDep, pFirstDep, pKthDep) ; + // se il path di test rispetta i vincoli, allora effettuo i cambi sul percorso vero + if ( IsPathRespectingContraints( m_pCheckDep)) { + SwapNodesForHybrid1( pLast, pFirst, pKth) ; + count = 0 ; + pFirst = pLast->pNext ; + pKth = pFirst->pNext ; + } + // altrimenti passo al nodo successivo + else + pKth = pKth->pNext ; + } } else { - for ( NODE* pReverse = pFirst ; pReverse != pKth ;) { - NODE* pSave = pReverse->pNext ; - pReverse->pNext = pReverse->pPrev ; - pReverse->pPrev = pSave ; - pReverse = pSave ; + // nel caso di assenza di dipendenze + if ( ! ExistConstraints()) { + SwapNodesForHybrid2( pLast, pFirst, pKth) ; + count = 0 ; + pFirst = pLast->pNext ; + pKth = pFirst->pNext ; + } + // nel caso di dipendeze, controllo se il nuovo percorso le rispetta + else { + CopyPathAdv( pPath, m_pCheckDep, pLast, pFirst, pKth, &pLastDep, &pFirstDep, &pKthDep) ; + // effettuo i cambi dei nodi sul path di test per le dipendenze + SwapNodesForHybrid2( pLastDep, pFirstDep, pKthDep) ; + // se il path di test rispetta i vincoli, allora effettuo i cambi sul percorso vero + if ( IsPathRespectingContraints( m_pCheckDep)) { + SwapNodesForHybrid2( pLast, pFirst, pKth) ; + count = 0 ; + pFirst = pLast->pNext ; + pKth = pFirst->pNext ; + } + // altrimenti passo al nodo successivo + else + pKth = pKth->pNext ; } - pFirst->pNext = pKth->pNext ; - pKth->pNext->pPrev = pFirst ; - pKth->pNext = pKth->pPrev ; - pKth->pPrev = pLast ; - pLast->pNext = pKth ; } - count = 0 ; - pFirst = pLast->pNext ; - pKth = pFirst->pNext ; - } + } else pKth = pKth->pNext ; } while ( pKth != pLast->pPrev->pPrev && count != 0) ; diff --git a/MachOptimization.cpp b/MachOptimization.cpp new file mode 100644 index 0000000..3afad5d --- /dev/null +++ b/MachOptimization.cpp @@ -0,0 +1,658 @@ +//---------------------------------------------------------------------------- +// EgalTech 2025-2025 +//---------------------------------------------------------------------------- +// File : MachOptimization.cpp Data : 31.03.25 Versione : 2.7a1 +// Contenuto : Classe per calcolo ottimizzato per le lavorazioni. +// +// +// Modifiche : 31.03.2025 RE Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "DllMain.h" +#include "MachOptimization.h" +#include "/EgtDev/Include/EGnStringUtils.h" +#include "/EgtDev/Include/EGkGeoConst.h" +#include "/EgtDev/Include/ENkShortestPath.h" +#include "/EgtDev/Include/EgtPointerOwner.h" +#include +#include + +/* Nomenclatura : + - TC = Tool Change ( cambio utensile ) +*/ + +#define _DEBUG_OPT 0 +#define _TIME_DEBUG 0 +#if _TIME_DEBUG + #include "/EgtDev/Include/EgtPerfCounter.h" +#endif + +using namespace std ; + +// Tempo massimo di lavorazione +static const int MAXTIME = 1073741823U ; +// Dato che tutte le distanze sono salvate come intere ( se ho 4 lavorazioni disposte sui vertici di un +// quadrato unitario, la distanza tra i vertici di uno stesso lato e tra i vertici delle diagonali +// risulta sempre 1, quindi moltiplico per COEFF in modo da avere una maggiore precisione. +// ( vertici su un lato -> dist = 1000, vertici sulla diagonale -> dist = 1414) +// invece che ( vertici su un lato -> dist = int( 1) = 1, vertici sulla diagonale -> dist = int( sqrt( 2)) = 1) +static const int COEFF = 1000 ; + +//---------------------------------------------------------------------------- +IMachOptimization* +CreateMachOptimization( void) +{ + return static_cast ( new( nothrow) MachOptimization) ; +} + +//---------------------------------------------------------------------------- +MachOptimization::MachOptimization( void) +{ + m_vMachOptm.clear() ; + m_vToolOptm.clear() ; + m_vDependences.clear() ; + m_vSuggDependences.clear() ; + m_dFeedL = 1. ; + m_dFeedA = 1. ; + m_bAllMandatory = false ; +} + +//---------------------------------------------------------------------------- +MachOptimization::~MachOptimization( void) +{ +} + +//---------------------------------------------------------------------------- +/* Funzione per inserire un Utensile */ +bool +MachOptimization::InsertTool( int nId, double dTCX, double dTCY, double dTCZ, double dTCA, + double dTCB, double dTCC, bool bX, bool bY, bool bZ, bool bA, + bool bB, bool bC, double dTLoad, double dTUnL) +{ + // Controllo se Id già presente + for ( const ToolOptm& currTool : m_vToolOptm) { + if ( currTool.nId == nId) { + LOG_ERROR( GetENkLogger(), ( "Duplicated Tool id : " + ToString( nId)).c_str()) ; + return false ; + } + } + // Tempi sempre positivi + if ( dTLoad < 0. || dTUnL < 0.) { + LOG_ERROR( GetENkLogger(), string( ( "Time must be positive :")).c_str()) ; + return false ; + } + // Inserisco il nuovo record + m_vToolOptm.emplace_back( nId, dTCX, dTCY, dTCZ, dTCA, dTCB, dTCC, bX, bY, bZ, bA, bB, bC, dTLoad, dTUnL) ; + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per inserire una Lavorazione */ +bool +MachOptimization::InsertMachining( int nId, int nToolId, int nGroup, + double dX_Start, double dY_Start, double dZ_Start, + double dA_Start, double dB_Start, double dC_Start, + double dX_End, double dY_End, double dZ_End, + double dA_End, double dB_End, double dC_End) +{ + // Controllo se Id già presente + for ( const MachOptm& currMach : m_vMachOptm) { + if ( currMach.nId == nId) { + LOG_ERROR( GetENkLogger(), ( "Duplicated Machining id : " + ToString( nId)).c_str()) ; + return false ; + } + } + // Controllo che l'utensile sia stato precedentemente inserito + bool bOk = false ; + for ( const ToolOptm& currTool : m_vToolOptm) { + bOk = currTool.nId == nToolId ; + if ( bOk) + break ; + } + if ( ! bOk) { + LOG_ERROR( GetENkLogger(), ( "Tool not found : " + ToString( nToolId)).c_str()) ; + return false ; + } + // Inserisco il nuovo record + m_vMachOptm.emplace_back( nId, nToolId, nGroup, dX_Start, dY_Start, dZ_Start, + dA_Start, dB_Start,dC_Start, dX_End, dY_End, dZ_End, dA_End, dB_End, dC_End) ; + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per l'inserimento delle dipendenze di ordine tra le lavorazioni [devono essere rispettate] + ( nIdPrev deve essere eseguita prima di nIdNext ) */ +bool +MachOptimization::InsertDependence( int nIdPrev, int nIdNext) +{ + // Check della dipendenza con quelle inserite + bool bExist = false ; + bool bConflict = false ; + if ( ! CheckDependences( nIdPrev, nIdNext, m_vDependences, false, bExist, bConflict)) + return false ; + // Se esiste di già, non faccio nulla + if ( bExist) + return true ; + // Se in conflitto, allora errore + if ( bConflict) + return false ; + // Inserisco la nuova dipendenza + m_vDependences.emplace_back( make_pair( nIdPrev, nIdNext)) ; + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per l'inserimento delle dipendenze di ordine tra le lavorazioni [si cerca di rispettarle] + ( nIdPrev dovrebbe essere eseguita prima di nIdNext ) */ +bool +MachOptimization::InsertSuggestedDependences( int nIdPrev, int nIdNext) +{ + // Check della dipendenza con quelle inserite + bool bExist = false ; + bool bConflict = false ; + if ( ! CheckDependences( nIdPrev, nIdNext, m_vSuggDependences, false, bExist, bConflict)) + return false ; + // Se già presente non faccio nulla + if ( bExist) + return true ; + // Inserisco la nuova dipendenza + m_vSuggDependences.emplace_back( make_pair( nIdPrev, nIdNext)) ; + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per impostare la prima Lavorazione */ +bool +MachOptimization::SetFirstMachining( int nId) +{ + // Controllo che l'Id sia presente + if ( find_if( m_vMachOptm.begin(), m_vMachOptm.end(), [&]( const MachOptm& Lav) { + return Lav.nId == nId ; + }) == m_vMachOptm.end()) { + LOG_ERROR( GetENkLogger(), ( "Machining id not found : " + ToString( nId)).c_str()) ; + return false ; + } + // Assegno la prima lavorazione + for ( int i = 0 ; i < int( m_vMachOptm.size()) ; ++ i) { + if ( m_vMachOptm[i].nId == nId) + continue ; + if ( ! InsertDependence( nId, m_vMachOptm[i].nId)) + return false ; + } + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per impostare l'ultima Lavorazione */ +bool +MachOptimization::SetLastMachining( int nId) +{ + // Controllo che l'Id sia presente + if ( find_if( m_vMachOptm.begin(), m_vMachOptm.end(), [&]( const MachOptm& Lav) { + return Lav.nId == nId ; + }) == m_vMachOptm.end()) { + LOG_ERROR( GetENkLogger(), ( "Machining id not found : " + ToString( nId)).c_str()) ; + return false ; + } + // Assegno l'ultima lavorazione + for ( int i = 0 ; i < int( m_vMachOptm.size()) ; ++ i) { + if ( m_vMachOptm[i].nId == nId) + continue ; + if ( ! InsertDependence( m_vMachOptm[i].nId, nId)) + return false ; + } + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per impostare la Feed Lineare e Angolare */ +bool +MachOptimization::SetFeeds( double dFeedL, double dFeedA) +{ + // Controllo dei valori + if ( dFeedL < EPS_ZERO || dFeedA < EPS_ZERO) { + LOG_ERROR( GetENkLogger(), string( " Feeds must be positive").c_str()) ; + return false ; + } + // Assegno le Feed + m_dFeedL = dFeedL ; + m_dFeedA = dFeedA ; + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per impostare ogni dipendenza da Gruppo come vincolo */ +bool +MachOptimization::SetAllGroupsAsMandatory( bool bAllMandatory) +{ + m_bAllMandatory = bAllMandatory ; + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione che assegna le dipendenze suggerite in base ai Gruppi */ +bool +MachOptimization::SetGroupDependences( void) +{ + // Se ho meno di due lavorazioni, non devo fare nulla + if ( int( m_vMachOptm.size()) < 2) + return true ; + // Recupero i diversi gruppi ( ordinati per priorità, quindi ordine decrescente) + set> setGroup ; + for ( const MachOptm& currLav : m_vMachOptm) + setGroup.insert( currLav.nGroup) ; + // Se presente un solo gruppo, non devo fare nulla + if ( int( setGroup.size()) == 1) + return true ; + // Scorro i gruppi ordinati per priorità e assegno le dipendenze suggerite + for ( auto it = setGroup.begin() ; it != setGroup.end() ; ++ it) { + // Cerco tutti gli indici delle lavorazioni con quella priorità e quelli con priorità inferiore + INTVECTOR vIndSamePriorityLav ; vIndSamePriorityLav.reserve( m_vMachOptm.size()) ; + INTVECTOR vIndLowerPriorityLav ; vIndLowerPriorityLav.reserve( m_vMachOptm.size()) ; + for ( int i = 0 ; i < int( m_vMachOptm.size()) ; ++ i) { + if ( m_vMachOptm[i].nGroup == *it) + vIndSamePriorityLav.emplace_back( i) ; + else if ( m_vMachOptm[i].nGroup < *it) + vIndLowerPriorityLav.emplace_back( i) ; + } + // Scorro le Lavorazioni che hanno la priorità massima corrente + for ( const int& nInd : vIndSamePriorityLav) { + // Scorro le Lavorazioni con priorità inferiore + for ( const int& nInd_LowerPriority : vIndLowerPriorityLav) { + // Assegno la dipendenze + if ( m_bAllMandatory) { + if ( ! InsertDependence( m_vMachOptm[nInd].nId, m_vMachOptm[nInd_LowerPriority].nId)) + return false ; + } + else { + if ( ! InsertSuggestedDependences( m_vMachOptm[nInd].nId, m_vMachOptm[nInd_LowerPriority].nId)) + return false ; + } + } + } + } + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per il controllo delle dipendenze inserite : se già presenti o se in conflitto */ +bool +MachOptimization::CheckDependences( int nIdPrev, int nIdNext, const INTINTVECTOR& vDep, bool bBlockMessages, bool& bExist, bool& bConflict) +{ + bExist = false ; + bConflict = false ; + // controllo che tali Id siano entrambi presenti tra le lavorazioni, in caso opposto errore + bool bOkIdPrev = false ; + bool bOkIdNext = false ; + for ( const MachOptm& currLav : m_vMachOptm) { + if ( ! bOkIdPrev && currLav.nId == nIdPrev) + bOkIdPrev = true ; + if ( ! bOkIdNext && currLav.nId == nIdNext) + bOkIdNext = true ; + if ( bOkIdPrev && bOkIdNext) + break ; + } + if ( ! bOkIdPrev || ! bOkIdNext) { + if ( ! bBlockMessages) + LOG_ERROR( GetENkLogger(), ( "Discharged Dependence, Id not found (" + ToString( nIdPrev) + "," + ToString( nIdNext) + ")").c_str()) ; + return false ; + } + + // controllo se la dipendenza è già stata inserita e che non sia presente una dipendeza inversa (conflitto) + // (... (Id_i, Id_j) , ... , ( Id_j, Id_i), ...) + for ( const auto& currDep : vDep) { + if ( currDep.first == nIdPrev && currDep.second == nIdNext) { + bExist = true ; + if ( ! bBlockMessages) + LOG_INFO( GetENkLogger(), ( "Already found Dependence (" + ToString( nIdPrev) + "," + ToString( nIdNext) + ")").c_str()) ; + return true ; + } + if ( currDep.second == nIdPrev && currDep.first == nIdNext) { + bConflict = true ; + if ( ! bBlockMessages) + LOG_ERROR( GetENkLogger(), ( "Conflict Dependence (" + ToString( nIdPrev) + "," + ToString( nIdNext) + ")").c_str()) ; + return true ; + } + } + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per il calcolo della distanza euclidea tra assi Lineari o Angolari di due lavorazioni + Considerando lo stesso utensile */ +double +MachOptimization::CalcTimeLavLav( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin) +{ + double dDist = 0. ; + double dFeed = EPS_SMALL ; + if ( bLin) { + /* Lav1 -- Feed Lineare --> Lav2 */ + dDist = sqrt( ( Lav2.dX_Start - Lav1.dX_End) * ( Lav2.dX_Start - Lav1.dX_End) + + ( Lav2.dY_Start - Lav1.dY_End) * ( Lav2.dY_Start - Lav1.dY_End) + + ( Lav2.dZ_Start - Lav1.dZ_End) * ( Lav2.dZ_Start - Lav1.dZ_End)) ; + dFeed = m_dFeedL ; + } + else { + /* Lav1 -- Feed Angolare --> Lav2 */ + dDist = sqrt( ( Lav2.dA_Start - Lav1.dA_End) * ( Lav2.dA_Start - Lav1.dA_End) + + ( Lav2.dB_Start - Lav1.dB_End) * ( Lav2.dB_Start - Lav1.dB_End) + + ( Lav2.dC_Start - Lav1.dC_End) * ( Lav2.dC_Start - Lav1.dC_End)) ; + dFeed = m_dFeedA ; + } + return COEFF * ( dDist / dFeed) ; +} + +//---------------------------------------------------------------------------- +/* Funzione per il calcolo della distanza euclidea tra assi Lineari o Angolari tra una lavorazione e + il suo TC */ +bool +MachOptimization::CalcTimeLavLavTC( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin, + double& dTimeLav1TC1, double& dTimeTC1TC2, double& dTimeTC2Lav2, + double& dTUnload1, double& dTLoad2) +{ + /* + dTimeLav1TC1 : Tempo tra EndPoint di Lav1 e Posizione di TC per Utensile di Lav1 + dTimeTC1TC2 : Tempo di cambio utensile tra Tool di Lav1 e Lav2 + dTimeTC2Lav2 : Tempo tra TC per utensile Lav2 e StartPoint di Lav2 + */ + dTimeLav1TC1 = 0. ; + dTimeTC1TC2 = 0. ; + dTimeTC2Lav2 = 0. ; + dTUnload1 = 0. ; + dTLoad2 = 0. ; + // Recupero Utensile Lav1 e Lav2 + int nIndToolLav1 = -1 ; + int nIndToolLav2 = -1 ; + bool bFoundToolLav1 = false ; + bool bFoundToolLav2 = false ; + for ( int i = 0 ; i < int( m_vToolOptm.size()) ; ++ i) { + bFoundToolLav1 = ( m_vToolOptm[i].nId == Lav1.nIdTool) ; + if ( bFoundToolLav1) + nIndToolLav1 = i ; + bFoundToolLav2 = ( m_vToolOptm[i].nId == Lav2.nIdTool) ; + if ( bFoundToolLav2) + nIndToolLav2 = i ; + if ( nIndToolLav1 != -1 && nIndToolLav2 != -1) + break ; + } + if ( nIndToolLav1 == -1) + LOG_INFO( GetENkLogger(), ( "Tool Not Found :" + ToString( Lav1.nIdTool)).c_str()) ; + if ( nIndToolLav2 == -1) + LOG_INFO( GetENkLogger(), ( "Tool Not Found :" + ToString( Lav2.nIdTool)).c_str()) ; + if ( nIndToolLav1 == -1 || nIndToolLav2 == -1) + return false ; + const ToolOptm& ToolLav1 = m_vToolOptm[nIndToolLav1] ; + const ToolOptm& ToolLav2 = m_vToolOptm[nIndToolLav2] ; + // Imposto i tempi di carico e scarico degli utensili + dTUnload1 = COEFF * ToolLav1.dTUnLoad ; + dTLoad2 = COEFF * ToolLav2.dTLoad ; + // Se distanza lineare + if ( bLin) { + // Come primo tempo considero quello tra la fine di Lav1 e TC1 + double dXi = Lav1.dX_End ; + double dXe = ( ToolLav1.bTCX ? ToolLav1.dTCX : dXi) ; + double dYi = Lav1.dY_End ; + double dYe = ( ToolLav1.bTCY ? ToolLav1.dTCY : dYi) ; + double dZi = Lav1.dZ_End ; + double dZe = ( ToolLav1.bTCZ ? ToolLav1.dTCZ : dZi) ; + double dDist = sqrt( ( dXe - dXi) * ( dXe - dXi) + ( dYe - dYi) * ( dYe - dYi) + ( dZe - dZi) * ( dZe - dZi)) ; + dTimeLav1TC1 = COEFF * ( dDist / m_dFeedL) ; + // Come secondo tempo considero quello tra TC1 e TC2 + dXi = dXe ; + dXe = ( ToolLav2.bTCX ? ToolLav2.dTCX : dXi) ; + dYi = dYe ; + dYe = ( ToolLav2.bTCY ? ToolLav2.dTCY : dYi) ; + dZi = dZe ; + dZe = ( ToolLav2.bTCZ ? ToolLav2.dTCZ : dZi) ; + dDist = sqrt( ( dXe - dXi) * ( dXe - dXi) + ( dYe - dYi) * ( dYe - dYi) + ( dZe - dZi) * ( dZe - dZi)) ; + dTimeTC1TC2 = COEFF * ( dDist / m_dFeedL) ; + // Come terzo tempo considero quello tra TC2 e Lav2 + dXi = dXe ; + dYe = Lav2.dX_Start ; + dYi = dYe ; + dYe = Lav2.dY_Start ; + dZi = dZe ; + dZe = Lav2.dZ_Start ; + dDist = sqrt( ( dXe - dXi) * ( dXe - dXi) + ( dYe - dYi) * ( dYe - dYi) + ( dZe - dZi) * ( dZe - dZi)) ; + dTimeTC2Lav2 = COEFF * ( dDist / m_dFeedL) ; + } + // Se distanza angolare + else { + // Come primo tempo considero quello tra la fine di Lav1 e TC1 + double dAi = Lav1.dA_Start ; + double dAe = ( ToolLav1.dTCA ? ToolLav1.dTCA : dAi) ; + double dBi = Lav1.dB_Start ; + double dBe = ( ToolLav1.dTCB ? ToolLav1.dTCB : dBi) ; + double dCi = Lav1.dC_Start ; + double dCe = ( ToolLav1.dTCC ? ToolLav1.dTCC : dCi) ; + double dDist = sqrt( ( dAe - dAi) * ( dAe - dAi) + ( dBe - dBi) * ( dBe - dBi) + ( dCe - dCi) * ( dCe - dCi)) ; + dTimeLav1TC1 = COEFF * ( dDist / m_dFeedA) ; + // Come secondo tempo considero quello tra TC1 e TC2 + dAi = dAe ; + dAe = ( ToolLav2.bTCA ? ToolLav2.dTCA : dAi) ; + dBi = dBe ; + dBe = ( ToolLav2.bTCB ? ToolLav2.dTCB : dBi) ; + dCi = dCe ; + dCe = ( ToolLav2.bTCC ? ToolLav2.dTCC : dCi) ; + dDist = sqrt( ( dAe - dAi) * ( dAe - dAi) + ( dBe - dBi) * ( dBe - dBi) + ( dCe - dCi) * ( dCe - dCi)) ; + dTimeTC1TC2 = COEFF * ( dDist / m_dFeedA) ; + // Come terzo tempo considero quello tra TC2 e Lav2 + dAi = dAe ; + dAe = Lav2.dA_Start ; + dBi = dBe ; + dBe = Lav2.dB_Start ; + dCi = dCe ; + dCe = Lav2.dC_Start ; + dDist = sqrt( ( dAe - dAi) * ( dAe - dAi) + ( dBe - dBi) * ( dBe - dBi) + ( dCe - dCi) * ( dCe - dCi)) ; + dTimeTC2Lav2 = COEFF * ( dDist / m_dFeedA) ; + } + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per calcolare il tempo nel passare dalla lavorazione nInd1-esima alla lavorazione + nInd2-esima */ +bool +MachOptimization::CalcMachTime( int nInd1, int nInd2, int& nTime) +{ + // Se Indice fuori dal range, errore + nTime = 0 ; + if ( nInd1 < 0 || nInd1 >= int( m_vMachOptm.size()) || + nInd2 < 0 || nInd2 >= int( m_vMachOptm.size())) + return false ; + + // Recupero riferimento delle due lavorazioni + MachOptm& Lav1 = m_vMachOptm[nInd1] ; + MachOptm& Lav2 = m_vMachOptm[nInd2] ; + + // Se l'utensile è lo stesso + if ( Lav1.nIdTool == Lav2.nIdTool) + nTime = int( max( CalcTimeLavLav( Lav1, Lav2, true), CalcTimeLavLav( Lav1, Lav2, false))) ; + else { + double dLTime1, dLTime2, dLTime3 ; + double dATime1, dATime2, dATime3 ; + double dTUnload1, dTLoad2 ; + if ( ! CalcTimeLavLavTC( Lav1, Lav2, true, dLTime1, dLTime2, dLTime3, dTUnload1, dTLoad2) || + ! CalcTimeLavLavTC( Lav1, Lav2, false, dATime1, dATime2, dATime3, dTUnload1, dTLoad2)) { + LOG_ERROR( GetENkLogger(), "Error in computing time") ; + return false ; + } + nTime += int( max( dLTime1, dATime1) + max( dLTime2, dATime2) + max( dLTime3, dATime3)) ; + // Sommo i tempi di carico e scarico dei due utensili + nTime += int( dTUnload1) + int( dTLoad2) ; + } + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per il calcolo della Matrice dei tempi (NxN), dove N = #Lavorazioni */ +bool +MachOptimization::ComputeTimeMatrix( INTMATRIX& mTimes) +{ + // Creo una copia dei vettori delle dipendenze ( le cancello di volta in volta che le assegno) + INTINTVECTOR vDep = m_vDependences ; + INTINTVECTOR vSuggDep = m_vSuggDependences ; + const int N = int( m_vMachOptm.size()) ; + // Matrice NxN ( con Flag per cella già calcolata) + mTimes.clear() ; + mTimes.resize( N) ; + for ( auto& Row : mTimes) + Row.resize( N) ; + // Calcolo i tempi tra tutte le lavorazioni + for ( int i = 0 ; i < N ; ++ i) { + for ( int j = 0 ; j < N ; ++ j) { + // Se sono sulla diagonale, allora il tempo è infinito + if ( i == j) { + mTimes[i][j] = MAXTIME ; + continue ; + } + // Se sono nella cella ( i, j), calcolo il tempo necessario + if ( ! CalcMachTime( i, j, mTimes[i][j])) + return false ; + } + } + #if _DEBUG_OPT + string sRowSplit = "" ; + for ( int i = 0 ; i < N ; ++ i) + sRowSplit += "----------" ; + for ( int i = -1 ; i < N ; ++ i) { + // Intestazione + if ( i == -1) { + LOG_INFO( GetENkLogger(), sRowSplit.c_str()) ; + string sCell = "**********" ; + for ( int j = 0 ; j < N ; ++ j) { + int nId = m_vMachOptm[j].nId ; + string sId = ToString( nId) ; + int nLen = sId.length() ; + for ( int k = 0 ; k < 10 - nLen ; ++ k) + sCell += " " ; + sCell += sId ; + } + LOG_INFO( GetENkLogger(), sCell.c_str()) ; + continue ; + } + // Prima riga della colonna + string sRow = "|" ; + int nId = m_vMachOptm[i].nId ; + string sId = ToString( nId) ; + int nLen = sId.length() ; + for ( int k = 0 ; k < 8 - nLen ; ++ k) + sRow += " " ; + sRow += sId ; + sRow += "|" ; + for ( int j = 0 ; j < N ; ++ j) { + double dTime = mTimes[i][j] ; + string sTime = ( dTime > MAXTIME - 1 ? "inf" : ToString( dTime, 1)) ; + int nLen = sTime.length() ; + for ( int k = 0 ; k < 9 - nLen ; ++ k) + sRow += " " ; + sRow += sTime ; + sRow += "|" ; + } + LOG_INFO( GetENkLogger(), sRow.c_str()) ; + } + LOG_INFO( GetENkLogger(), sRowSplit.c_str()) ; + #endif + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione che restituisce la posizione della lavorazione di Id pari a nId nel vettore delle lavorazioni */ +int +MachOptimization::GetIndById( int nId) +{ + // Scorro le lavorazioni + for ( int i = 0 ; i < int( m_vMachOptm.size()) ; ++ i) { + if ( m_vMachOptm[i].nId == nId) + return i ; + } + return -1 ; +} + +//---------------------------------------------------------------------------- +bool +MachOptimization::GetResult( INTVECTOR& vIds) +{ + // Se non ho lavorazioni, allora non faccio nulla + vIds.clear() ; + if ( m_vMachOptm.empty()) + return true ; + // Se ho una sola lavorazione allora ritorno se stessa + if ( int( m_vMachOptm.size()) == 1) { + vIds.push_back( m_vMachOptm[0].nId) ; + return true ; + } + // Inserisco le lavorazioni per ordine di inserimento + for ( const MachOptm& myLav : m_vMachOptm) + vIds.push_back( myLav.nId) ; + // Inizializzo le dipendenze suggerite dai gruppi di lavorazione + if ( ! SetGroupDependences()) { + LOG_ERROR( GetENkLogger(), "Error in defining Group depedences") ; + return false ; + } + // Calcolo la matrice dei tempi + INTMATRIX mTimes ; + if ( ! ComputeTimeMatrix( mTimes)) { + LOG_ERROR( GetENkLogger(), "Error in Computing Time Matrix") ; + return false ; + } + // Istanzio il Problema ShortesPath con vincoli + PtrOwner mySP( CreateShortestPath()) ; + if ( IsNull( mySP)) + return false ; + // Per ogni lavorazione inserisco gli estremi + for ( const MachOptm& Lav : m_vMachOptm) { + if ( ! mySP->AddPoint( Lav.dX_Start, Lav.dY_Start, Lav.dZ_End, 0., 0., + Lav.dX_End, Lav.dY_End, Lav.dZ_End, 0., 0.)) { + LOG_ERROR( GetENkLogger(), "Error adding Machining Bounds") ; + return false ; + } + } + // Assegno la matrice delle distanze calcolata in precedenza + if ( ! mySP->SetDistMatrix( mTimes)) { + LOG_ERROR( GetENkLogger(), "Error setting distances matrix") ; + return false ; + } + // Per ogni dipendeza obbligatoria assegno un vincolo + for ( const INTINT& nnDep : m_vDependences) { + if ( ! mySP->SetConstraintOrder( GetIndById( nnDep.first), GetIndById( nnDep.second))) { + LOG_ERROR( GetENkLogger(), "Error in adding Constraints") ; + return false ; + } + } + // Aggiungo le dipendenza suggerite + for ( const INTINT& nnSuggDep : m_vSuggDependences) { + if ( ! mySP->SetSuggestedOrder( GetIndById( nnSuggDep.first), GetIndById( nnSuggDep.second))) { + LOG_ERROR( GetENkLogger(), "Error in adding Suggested Dependence") ; + return false ; + } + } + // Calcolo la soluzione + #if _TIME_DEBUG + PerformanceCounter PC ; PC.Start() ; + #endif + if ( ! mySP->Calculate( IShortestPath::SP_OPEN)) { + LOG_ERROR( GetENkLogger(), "Error in computing solution") ; + return false ; + } + #if _TIME_DEBUG + string sTime = string( to_string( m_vDependences.size()) + string( "Time : ") + ToString( PC.Stop())) ; + LOG_INFO( GetENkLogger(), sTime.c_str()) ; + #endif + // Recupero l'ordine + INTVECTOR U ; + if ( ! mySP->GetOrder( U)) { + LOG_ERROR( GetENkLogger(), "Error in ordering") ; + return false ; + } + // Restituisco gli Id ordinati + vIds.resize( m_vMachOptm.size()) ; + for ( int nInd = 0 ; nInd < int( U.size()) ; ++ nInd) + vIds[nInd] = m_vMachOptm[U[nInd]].nId ; + return true ; +} + diff --git a/MachOptimization.h b/MachOptimization.h new file mode 100644 index 0000000..947ac1b --- /dev/null +++ b/MachOptimization.h @@ -0,0 +1,73 @@ +//---------------------------------------------------------------------------- +// EgalTech 2025-2025 +//---------------------------------------------------------------------------- +// File : MachOptimization.h Data : 31.03.25 Versione : 2.7a1 +// Contenuto : Classe per calcolo ottimizzato per le lavorazioni. +// +// +// Modifiche : 31.03.2025 RE Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +#pragma once + +#include "/EgtDev/Include/ENkMachOptimization.h" + +//---------------------------------------------------------------------------- +class MachOptimization : public IMachOptimization +{ + public : + ~MachOptimization( void) override ; + // Inserimento + bool InsertTool( int nId, double dTCX, double dTCY, double dTCZ, double dTCA, + double dTCB, double dTCC, bool bX, bool bY, bool bZ, bool bA, + bool bB, bool bC, double dTLoad, double dTUnL) override ; + bool InsertMachining( int nId, int nToolId, int nGroup, + double dX_Start, double dY_Start, double dZ_Start, + double dA_Start, double dB_Start, double dC_Start, + double dX_End, double dY_End, double dZ_End, + double dA_End, double dB_End, double dC_End) override ; + bool InsertDependence( int nIdPrec, int nIdNext) override ; + bool InsertSuggestedDependences( int nIdPrec, int nIdNext) override ; + + // Impostazione parametri lavorazione + bool SetFirstMachining( int nId) override ; + bool SetLastMachining( int nId) override ; + bool SetFeeds( double dFeedL, double dFeedA) override ; + bool SetAllGroupsAsMandatory( bool bAllMandatory) override ; + + // Risultati + bool GetResult( INTVECTOR& vIds) override ; + + public : + MachOptimization( void) ; + + private : + // Calcolo dei tempi tra le lavorazioni + double CalcTimeLavLav( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin) ; + bool CalcTimeLavLavTC( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin, + double& dTimeLav1TC1, double& dTimeTC1TC2, double& dTimeTC2Lav2, + double& dTUnload1, double& dTLoad2) ; + bool CalcMachTime( int nInd1, int nInd2, int& nTime) ; + + // Controllo Conflitti tra dipendenze e Gestione Dipendenze + bool CheckDependences( int nIdPrev, int nIdNext, const INTINTVECTOR& vDep, + bool bBlockMessages, bool& bExist, bool& bConflict) ; + bool SetGroupDependences( void) ; + + // Calcolo della Matrice dei tempi, Del fattore perso dipendenze suggerite e della soluzione ottimale + bool ComputeTimeMatrix( INTMATRIX& mTimes) ; + + // Utility + int GetIndById( int nId) ; + + private : + MACHOPTMVECTOR m_vMachOptm ; // Vettore delle lavorazioni + TOOLOPTMVECTOR m_vToolOptm ; // Vettore degli utensili + INTINTVECTOR m_vDependences ; // Vettore di dipendenze ( IdPrev, IdSucc) -> deve essere rispettato + INTINTVECTOR m_vSuggDependences ; // Vettore delle dipendenze suggerite ( IdPrev, IdSucc) -> si cerca di rispettarle + double m_dFeedL ; // Feed Lineare + double m_dFeedA ; // Feed Angolare + bool m_bAllMandatory ; // Flag per impostare ogni dipendenza da Gruppo come vincolo +} ; diff --git a/Nn.cpp b/Nn.cpp index 7c2773a..44220c2 100644 --- a/Nn.cpp +++ b/Nn.cpp @@ -26,34 +26,59 @@ ShortestPath::NearNeighbor( void) m_Available[ Index( i, i)] = false ; } - // cerco il nodo con il più corto arco che se ne diparte - unsigned nCurr = 0 ; - unsigned nNew = CheapArc( nCurr) ; - for ( unsigned i = 1 ; i < m_nNumPnts ; ++ i) { - unsigned j = CheapArc( i) ; - if ( ArcCost( i, j) < ArcCost( nCurr, nNew)) { - nCurr = i ; - nNew = j ; + // se non ho dipendenze + if ( ! ExistConstraints()) { + // cerco il nodo con il più corto arco che se ne diparte + unsigned nCurr = 0 ; + unsigned nNew = CheapArc( nCurr) ; + for ( unsigned i = 1 ; i < m_nNumPnts ; ++ i) { + unsigned j = CheapArc( i) ; + if ( ArcCost( i, j) < ArcCost( nCurr, nNew)) { + nCurr = i ; + nNew = j ; + } } + // imposto il nodo corrente come base del percorso + NODE* pPath = m_pMain ; + pPath->nPos = nCurr ; + // ciclo + do { + // imposto tutti gli archi non disponibili al nodo corrente + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) + m_Available[ Index( i, nCurr)] = false ; + // aggiungo un nuovo nodo al percorso + pPath = pPath->pNext ; + pPath->nPos = nNew ; + // imposto il nuovo nodo come nodo corrente + nCurr = nNew ; + // cerco il nuovo nodo all'estremo dell'arco più corto dal nodo corrente + nNew = CheapArc( nCurr) ; + } while ( m_Available[ Index( nCurr, nNew)]) ; } - - // imposto il nodo corrente come base del percorso - NODE* pPath = m_pMain ; - pPath->nPos = nCurr ; - - // ciclo - do { - // imposto tutti gli archi non disponibili al nodo corrente - for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) - m_Available[ Index( i, nCurr)] = false ; - // aggiungo un nuovo nodo al percorso - pPath = pPath->pNext ; + // se esistono dipendenze + else { + // cerco il primo nodo da cui iniziare + unsigned nNew = -1 ; + if ( ! ChooseFirstNode( nNew)) + return MAXDIST ; + // imposto il nodo corrente come base del percorso + NODE* pPath = m_pMain ; pPath->nPos = nNew ; - // imposto il nuovo nodo come nodo corrente - nCurr = nNew ; - // cerco il nuovo nodo all'estremo dell'arco più corto dal nodo corrente - nNew = CheapArc( nCurr) ; - } while ( m_Available[ Index( nCurr, nNew)]) ; + unsigned nSize = 1 ; + // ciclo + do { + // imposto tutti gli archi non disponibili al nodo corrente + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) + m_Available[ Index( i, nNew)] = false ; + // cerco il nuovo nodo all'estremo dell'arco più corto dal nodo corrente + if ( ! ChooseNextNode( pPath, nSize, nNew, true)) + return MAXDIST ; + // aggiungo un nuovo nodo al percorso + pPath = pPath->pNext ; + pPath->nPos = nNew ; + ++ nSize ; + } while ( nSize < m_nNumPnts) ; + } // calcolo il costo del percorso return TotalCost( m_pMain) ; @@ -73,6 +98,5 @@ ShortestPath::CheapArc( unsigned i) if ( ArcCost( i, k) < ArcCost( i, j)) j = k ; } - return j ; } diff --git a/PntOpt.cpp b/PntOpt.cpp index b3b16b4..e978a3c 100644 --- a/PntOpt.cpp +++ b/PntOpt.cpp @@ -20,6 +20,17 @@ #include "stdafx.h" #include "ShortestPath.h" +static void +SwapNodesForPntOpt( NODE* pFirst, NODE* pLast, NODE* pBest) +{ + pFirst->pPrev = pLast->pPrev ; + pLast->pPrev->pNext = pFirst ; + pLast->pPrev = pBest ; + pLast->pNext = pBest->pNext ; + pBest->pNext = pLast ; + pLast->pNext->pPrev = pLast ; +} + /* ------------------------------------------------------------------------- */ long long unsigned ShortestPath::PointOpt( NODE* pPath) @@ -27,33 +38,62 @@ ShortestPath::PointOpt( NODE* pPath) // ciclo di tentativi di spostamento di un punto unsigned nCount = 1 ; NODE* pFirst = pPath ; + NODE* pFirstDep = m_pCheckDep ; do { unsigned nBestImprove = 0 ; NODE* pBest = nullptr ; NODE* pLast = pFirst->pPrev ; + NODE* pLastDep = pFirstDep->pPrev ; + NODE* pKthDep = pFirstDep ; for ( NODE* pKth = pFirst ; pKth != pLast->pPrev->pPrev ; pKth = pKth->pNext) { + // se non esistono dipendenze suggerite unsigned nEXIST = ArcCost( pLast->pPrev->nPos, pLast->nPos) + ArcCost( pLast->nPos, pFirst->nPos) + ArcCost( pKth->nPos, pKth->pNext->nPos) ; unsigned nTEST = ArcCost( pLast->pPrev->nPos, pFirst->nPos) + ArcCost( pKth->nPos, pLast->nPos) + ArcCost( pLast->nPos, pKth->pNext->nPos) ; - if ( nTEST < nEXIST && ( nEXIST - nTEST) > nBestImprove) { - nBestImprove = nEXIST - nTEST ; - pBest = pKth ; + if ( ! ExistSuggDependences()) { + if ( nTEST < nEXIST && ( nEXIST - nTEST) > nBestImprove) { + // nel caso di assenza di dipendenze + if ( ! ExistConstraints()) { + nBestImprove = nEXIST - nTEST ; + pBest = pKth ; + } + // nel caso di dipendenze + else { + CopyPathAdv( pPath, m_pCheckDep, pLast, pFirst, pKth, &pLastDep, &pFirstDep, &pKthDep) ; + SwapNodesForPntOpt( pFirstDep, pLastDep, pKthDep) ; + if ( IsPathRespectingContraints( m_pCheckDep)) { + nBestImprove = nEXIST - nTEST ; + pBest = pKth ; + } + } + } + } + // se esistono dipendenze suggerite + else { + CopyPathAdv( pPath, m_pCheckDep, pLast, pFirst, pKth, &pLastDep, &pFirstDep, &pKthDep) ; + SwapNodesForPntOpt( pFirstDep, pLastDep, pKthDep) ; + if ( IsPathRespectingContraints( m_pCheckDep)) { + int nBreak ; + IsPathRespectingSuggestedDependences( pPath, nBreak) ; + nEXIST += m_nMaxDistSuggDep * nBreak ; + IsPathRespectingSuggestedDependences( m_pCheckDep, nBreak) ; + nTEST += m_nMaxDistSuggDep * nBreak ; + if ( nTEST < nEXIST && ( nEXIST - nTEST) > nBestImprove) { + nBestImprove = nEXIST - nTEST ; + pBest = pKth ; + } + } } } if ( nBestImprove == 0) { pFirst = pFirst->pNext ; nCount ++ ; - } + } else { - pFirst->pPrev = pLast->pPrev ; - pLast->pPrev->pNext = pFirst ; - pLast->pPrev = pBest ; - pLast->pNext = pBest->pNext ; - pBest->pNext = pLast ; - pLast->pNext->pPrev = pLast ; + SwapNodesForPntOpt( pFirst, pLast, pBest) ; nCount = 1 ; } } while ( nCount < m_nNumPnts) ; diff --git a/ShortestPath.cpp b/ShortestPath.cpp index e4f2372..ad8e5af 100644 --- a/ShortestPath.cpp +++ b/ShortestPath.cpp @@ -25,7 +25,7 @@ using namespace std ; IShortestPath* CreateShortestPath( void) { - return static_cast ( new(nothrow) ShortestPath) ; + return static_cast ( new(nothrow) ShortestPath) ; } //---------------------------------------------------------------------------- @@ -45,6 +45,12 @@ ShortestPath::ShortestPath( void) m_Available = nullptr ; m_pMain = nullptr ; m_pDupl = nullptr ; + m_pCheckDep = nullptr ; + m_vOrderConstr.clear() ; + m_vOrderSugg.clear() ; + m_vOrderSuggRowMap.clear() ; + m_nMaxDistSuggDep = 0 ; + m_ExtDistMat.clear() ; } //---------------------------------------------------------------------------- @@ -62,6 +68,9 @@ ShortestPath::~ShortestPath( void) if ( m_pDupl != nullptr) delete m_pDupl ; m_pDupl = nullptr ; + if ( m_pCheckDep != nullptr) + delete m_pCheckDep ; + m_pCheckDep = nullptr ; } //---------------------------------------------------------------------------- @@ -235,3 +244,35 @@ ShortestPath::GetMinLength( double& dMinLen) dMinLen = ( double)m_nMinCost ; return true ; } + +//---------------------------------------------------------------------------- +bool +ShortestPath::SetDistMatrix( const INTMATRIX& mDistMatrix) +{ + // Assegno la matrice suggerita + m_ExtDistMat = mDistMatrix ; + return true ; +} + +/* Funzioni per _dubug_ */ +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +void +ShortestPath::_printPath( NODE* pPath, string sFun) +{ + #if ENABLE_DEBUG + { + LOG_INFO( GetENkLogger(), sFun.c_str()) ; + NODE* pCurr = pPath ; + string sText = "" ; + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { + sText += ( ( i == 0 ? "" : " - ") + ToString( pCurr->nPos)) ; + pCurr = pCurr->pNext ; + } + LOG_INFO( GetENkLogger(), sText.c_str()) ; + } + #else + return ; + #endif +} diff --git a/ShortestPath.h b/ShortestPath.h index 630b686..e13da9b 100644 --- a/ShortestPath.h +++ b/ShortestPath.h @@ -14,9 +14,11 @@ #pragma once #include "/EgtDev/Include/ENkShortestPath.h" +#include //---------------------------------------------------------------------------- #define MAXDIST 1073741823U // 2^30 - 1 per evitare overflow +#define ENABLE_DEBUG 0 //---------------------------------------------------------------------------- struct SpPoint { @@ -63,6 +65,9 @@ class ShortestPath : public IShortestPath bool Calculate( int nType) override ; bool GetOrder( INTVECTOR& vOrder) override ; bool GetMinLength( double& dMinLen) override ; + bool SetDistMatrix( const INTMATRIX& mDistMatrix) override ; + bool SetConstraintOrder( int nPrev, int nNext) override ; + bool SetSuggestedOrder( int nPrev, int nNext) override ; public : ShortestPath( void) ; @@ -75,6 +80,10 @@ class ShortestPath : public IShortestPath void PreparePath( NODE* pPath) ; void SavePath( NODE* pPath) ; void RestorePath( NODE* pPath) ; + void CopyPath( NODE* pPath, NODE* pPathCopy) ; + void CopyPathAdv( NODE* pPath, NODE* pPathCopy, + NODE* pLast, NODE* pFirst, NODE* pKth, + NODE** pLastCopy, NODE** pFirstCopy, NODE** pKthCopy) ; long long unsigned TotalCost( NODE* pPath) ; void UpdateOrder( NODE* pPath) ; unsigned Index( unsigned i, unsigned j) @@ -90,6 +99,21 @@ class ShortestPath : public IShortestPath long long unsigned Hybrid( NODE* pPath) ; bool ZigZag( void) ; long long unsigned TotalCost( void) ; + bool IsViolatingContraints( int nPrev, int nNext) ; + bool IsPathRespectingContraints( NODE* pPath) ; + bool IsChangingStartPathRespectingConstraints( NODE* pPath, NODE* pFirst) ; + bool IsPathRespectingSuggestedDependences( NODE* pPath, int& nBreak) ; + unsigned CalcMaxDist( void) ; + bool ExistConstraints( void) + { return ! ( m_vOrderConstr.empty()) ; } + bool ExistSuggDependences( void) + { return ! ( m_vOrderSugg.empty()) ; } + bool ChooseFirstNode( unsigned& iFirst) ; + bool ChooseNextNode( NODE* pPath, unsigned nSize, unsigned& j, bool bNearVsFar) ; + + // _debug + void _printPath( NODE* pPath, std::string sFun) ; + private : static const int MAX_SPPS = 32766 ; // 2^15-2 per evitare problemi nell'allocazione di m_Dists a 32bit @@ -111,6 +135,15 @@ class ShortestPath : public IShortestPath bool* m_Available ; NODE* m_pMain ; NODE* m_pDupl ; + NODE* m_pCheckDep ; long long unsigned m_nTotMin ; long long unsigned m_nMinCost ; + INTINTVECTOR m_vOrderConstr ; + INTINTVECTOR m_vOrderSugg_tmp ; + + BOOLVECTOR m_vOrderSugg ; + BOOLVECTOR m_vOrderSuggRowMap ; + + unsigned m_nMaxDistSuggDep ; + INTMATRIX m_ExtDistMat ; } ; diff --git a/ShortestPathTsp.cpp b/ShortestPathTsp.cpp index a4560c2..0272f33 100644 --- a/ShortestPathTsp.cpp +++ b/ShortestPathTsp.cpp @@ -54,7 +54,7 @@ ShortestPath::Tsp( void) m_Dists = new(nothrow) unsigned[ m_nNumPnts * m_nNumPnts] ; if ( m_Dists == nullptr) return false ; - // alloco la matrice ausiliaria + // alloco la matrice ausiliaria if ( m_Available != nullptr) delete m_Available ; m_Available = new(nothrow) bool[ m_nNumPnts * m_nNumPnts] ; @@ -72,6 +72,10 @@ ShortestPath::Tsp( void) m_pDupl = new(nothrow) NODE[ m_nNumPnts] ; if ( m_pDupl == nullptr) return false ; + // alloco la copia del path per controllo dipendenze + if ( m_pCheckDep != nullptr) + delete m_pCheckDep ; + m_pCheckDep = new(nothrow) NODE[ m_nNumPnts] ; // calcolo la matrice delle distanze CalcDistances() ; @@ -79,6 +83,7 @@ ShortestPath::Tsp( void) // organizzo percorsi PreparePath( m_pMain) ; PreparePath( m_pDupl) ; + PreparePath( m_pCheckDep) ; // inizializzo minimo costo m_nMinCost = LLONG_MAX ; @@ -87,6 +92,7 @@ ShortestPath::Tsp( void) long long unsigned nMinNN = NearNeighbor() ; string sOut = "-- NearNeighbor : TotalCost = " + to_string( nMinNN) ; MY_LOG( sOut.c_str()) ; + _printPath( m_pMain, ToString( "NN : ")) ; // se migliora, salvo l'ordinamento if ( nMinNN < m_nMinCost) { MY_LOG( " Improve") ; @@ -101,38 +107,42 @@ ShortestPath::Tsp( void) // ---- Applico percorso invertito, se risultato precedente scarso ---- const double COEFF_INVERTED = 1.2 ; const int MIN_PNTS_INVERTED = 128 ; - if ( m_nMinCost > COEFF_INVERTED * m_nTotMin || m_nNumPnts < MIN_PNTS_INVERTED) { - // inverto il percorso - NODE* pCurr = m_pDupl->pPrev ; - NODE* pPath = m_pMain ; - for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { - pPath->nPos = pCurr->nPos ; - pPath = pPath->pNext ; - pCurr = pCurr->pPrev ; + if ( ! ExistConstraints()) { + if ( m_nMinCost > COEFF_INVERTED * m_nTotMin || m_nNumPnts < MIN_PNTS_INVERTED) { + // inverto il percorso + NODE* pCurr = m_pDupl->pPrev ; + NODE* pPath = m_pMain ; + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { + pPath->nPos = pCurr->nPos ; + pPath = pPath->pNext ; + pCurr = pCurr->pPrev ; + } + // ne calcolo il risultato + long long unsigned nMinInv = TotalCost( m_pMain) ; + _printPath( m_pMain, "Inverted NN : ") ; + string sOut = "-- Inverted NN : TotalCost = " + to_string( nMinInv) ; + MY_LOG( sOut.c_str()) ; + // se migliora, salvo l'ordinamento + if ( nMinInv < m_nMinCost) { + MY_LOG( " Improve") ; + m_nMinCost = nMinInv ; + UpdateOrder( m_pMain) ; + } + // salvo il percorso in un duplicato + SavePath( m_pMain) ; + // miglioramenti aggiuntivi + CalculateImprovements( m_pMain) ; } - // ne calcolo il risultato - long long unsigned nMinInv = TotalCost( m_pMain) ; - string sOut = "-- Inverted NN : TotalCost = " + to_string( nMinInv) ; - MY_LOG( sOut.c_str()) ; - // se migliora, salvo l'ordinamento - if ( nMinInv < m_nMinCost) { - MY_LOG( " Improve") ; - m_nMinCost = nMinInv ; - UpdateOrder( m_pMain) ; - } - // salvo il percorso in un duplicato - SavePath( m_pMain) ; - // miglioramenti aggiuntivi - CalculateImprovements( m_pMain) ; + else + MY_LOG( "-- Inverted NN : Not necessary") ; } - else - MY_LOG( "-- Inverted NN : Not necessary") ; // ---- Applico pessima partenza, se risultato precedente scarso ---- const double COEFF_FARNEIG = 1.4 ; const int MIN_PNTS_FARNEIG = 128 ; if ( m_nMinCost > COEFF_FARNEIG * m_nTotMin || m_nNumPnts < MIN_PNTS_FARNEIG) { long long unsigned nMinFN = FarNeighbor() ; + _printPath( m_pMain, "FN : ") ; string sOut = "-- FarNeighbor : TotalCost = " + to_string( nMinFN) ; MY_LOG( sOut.c_str()) ; // se migliora, salvo l'ordinamento @@ -159,6 +169,7 @@ ShortestPath::CalculateImprovements( NODE* pPath) // Ottimizzazione con movimento di singolo punto RestorePath( pPath) ; long long unsigned nPntOpt = PointOpt( pPath) ; + _printPath( pPath, "PointOpt : ") ; string sOut = " PointOpt : TotalCost = " + to_string( nPntOpt) ; MY_LOG( sOut.c_str()) ; // se migliora, salvo l'ordinamento @@ -173,6 +184,7 @@ ShortestPath::CalculateImprovements( NODE* pPath) if ( m_nNumPnts <= MAX_NODES_2OPT) { RestorePath( pPath) ; long long unsigned nTwoOpt = TwoOpt( pPath) ; + _printPath( pPath, "TwoOpt : ") ; string sOut = " TwoOpt : TotalCost = " + to_string( nTwoOpt) ; MY_LOG( sOut.c_str()) ; // se migliora, salvo l'ordinamento @@ -188,6 +200,7 @@ ShortestPath::CalculateImprovements( NODE* pPath) if ( m_nNumPnts <= MAX_NODES_HYBRID) { RestorePath( pPath) ; long long unsigned nHybOpt = Hybrid( pPath) ; + _printPath( pPath, "Hybrid : ") ; string sOut = " Hybrid : TotalCost = " + to_string( nHybOpt) ; MY_LOG( sOut.c_str()) ; // se migliora, salvo l'ordinamento @@ -342,29 +355,40 @@ ShortestPath::CalcDistances( void) } // ciclo per calcolare le distanze tra tutte le coppie di nodi + bool bCalcDistances = ( m_ExtDistMat.empty()) ; for ( unsigned i = 0 ; i < nLimit ; ++ i) { unsigned nRowMinDist = INT_MAX ; for ( unsigned j = 0 ; j < nLimit ; ++ j) { - if ( i != j) { - // calcolo distanza i -> j - float dDx = float( m_Points[i].dXf - m_Points[j].dXi) ; - float dDy = float( m_Points[i].dYf - m_Points[j].dYi) ; - float dDz = float( m_Points[i].dZf - m_Points[j].dZi) ; - float dDh = float( m_Points[i].dHf - m_Points[j].dHi) ; - float dDv = float( m_Points[i].dVf - m_Points[j].dVi) ; - unsigned nDist = min( GetDistance( dDx, dDy, dDz, dDh, dDv), MAXDIST) ; - m_Dists[ Index( i, j)] = nDist ; - // verifico se nuovo minimo di linea - if ( nDist < nRowMinDist) - nRowMinDist = nDist ; + // se ho già impostato una matrice delle distanze, allora uso quella + if ( ! bCalcDistances) + m_Dists[Index( i, j)] = m_ExtDistMat[i][j] ; + // altrimenti calcolo le rispettive distanze + else { + if ( i != j) { + // calcolo distanza i -> j + float dDx = float( m_Points[i].dXf - m_Points[j].dXi) ; + float dDy = float( m_Points[i].dYf - m_Points[j].dYi) ; + float dDz = float( m_Points[i].dZf - m_Points[j].dZi) ; + float dDh = float( m_Points[i].dHf - m_Points[j].dHi) ; + float dDv = float( m_Points[i].dVf - m_Points[j].dVi) ; + unsigned nDist = min( GetDistance( dDx, dDy, dDz, dDh, dDv), MAXDIST) ; + m_Dists[ Index( i, j)] = nDist ; + // verifico se nuovo minimo di linea + if ( nDist < nRowMinDist) + nRowMinDist = nDist ; + } + else + m_Dists[ Index( i, j)] = MAXDIST ; } - else - m_Dists[ Index( i, j)] = MAXDIST ; } // aggiorno il totale delle minime distanze m_nTotMin += nRowMinDist ; } + // nel caso di dipendenze suggerite, calcolo il coefficiente di costo massimo associato + if ( ExistSuggDependences()) + m_nMaxDistSuggDep = CalcMaxDist() ; + // nel caso di percorso aperto senza vincoli, il numero delle distanze è uno meno di quello dei veri nodi if ( m_nType == SP_OPEN && m_ObStart.nF == OB_NONE && m_ObEnd.nF == OB_NONE) @@ -423,6 +447,38 @@ ShortestPath::RestorePath( NODE* pPath) } } +//---------------------------------------------------------------------------- +void +ShortestPath::CopyPath( NODE* pPath, NODE* pPathCopy) +{ + NODE* pCurr = pPathCopy ; + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { + pCurr->nPos = pPath->nPos ; + pPath = pPath->pNext ; + pCurr = pCurr->pNext ; + } +} + +//---------------------------------------------------------------------------- +void +ShortestPath::CopyPathAdv( NODE* pPath, NODE* pPathCopy, NODE* pLast, NODE* pFirst, NODE* pKth, + NODE** pLastCopy, NODE** pFirstCopy, NODE** pKthCopy) +{ + CopyPath( pPath, pPathCopy) ; + NODE* pCurr = pPath ; + NODE* pCurrCopy = pPathCopy ; + for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) { + if ( pCurr == pLast) + *pLastCopy = pCurrCopy ; + if ( pCurr == pKth) + *pKthCopy = pCurrCopy ; + if ( pCurr == pFirst) + *pFirstCopy = pCurrCopy ; + pCurr = pCurr->pNext ; + pCurrCopy = pCurrCopy->pNext ; + } +} + //---------------------------------------------------------------------------- long long unsigned ShortestPath::TotalCost( NODE* pPath) @@ -438,6 +494,10 @@ ShortestPath::TotalCost( NODE* pPath) nCost += ArcCost( pCurr->nPos, pCurr->pNext->nPos) ; pCurr = pCurr->pNext ; } + // se sono presenti delle dipendenze suggerite violate, aumento il costo + int nBreak = 0 ; + if ( ! IsPathRespectingSuggestedDependences( pPath, nBreak)) + nCost += m_nMaxDistSuggDep * nBreak ; return nCost ; } diff --git a/TwoOpt.cpp b/TwoOpt.cpp index 1b35da8..192b9b2 100644 --- a/TwoOpt.cpp +++ b/TwoOpt.cpp @@ -21,6 +21,27 @@ #include "stdafx.h" #include "ShortestPath.h" +// ---------------------------------------------------------------------------- +static void +SwapNodesForTwoOpt( NODE* pFirst, NODE* pKth, NODE* pLast) +{ + // inverto il tratto intermedio + for ( NODE* pCurr = pFirst ; pCurr != pKth ; ) { + NODE* pSave = pCurr->pNext ; + pCurr->pNext = pCurr->pPrev ; + pCurr->pPrev = pSave ; + pCurr = pSave ; + } + // sistemo gli estremi + pFirst->pNext = pKth->pNext ; + pKth->pNext->pPrev = pFirst ; + pKth->pNext = pKth->pPrev ; + pKth->pPrev = pLast ; + pLast->pNext = pKth ; + pFirst = pLast->pNext ; + pKth = pFirst->pNext ; +} + // ---------------------------------------------------------------------------- long long unsigned ShortestPath::TwoOpt( NODE* pPath) @@ -29,44 +50,72 @@ ShortestPath::TwoOpt( NODE* pPath) unsigned nTry = 1 ; unsigned nCount = 1 ; NODE* pFirst = pPath ; + NODE* pFirstDep = m_pCheckDep ; do { NODE* pLast = pFirst->pPrev ; NODE* pKth = pFirst->pNext ; + NODE* pLastDep = pFirstDep->pPrev ; + NODE* pKthDep = pFirstDep->pNext ; do { - // lunghezza originale dei tratti da scambiare - unsigned nEXIST = ArcCost( pLast->nPos, pFirst->nPos) + - ArcCost( pKth->nPos, pKth->pNext->nPos) ; - // nuova lunghezza dei tratti da scambiare - unsigned nTEST = ArcCost( pFirst->nPos, pKth->pNext->nPos) + - ArcCost( pLast->nPos, pKth->nPos) ; - // lunghezze originali e nuova del tratto intermedio che viene invertito - for ( NODE* pCurr = pFirst ; pCurr != pKth ; pCurr = pCurr->pNext) { - nEXIST += ArcCost( pCurr->nPos, pCurr->pNext->nPos) ; - nTEST += ArcCost( pCurr->pNext->nPos, pCurr->nPos) ; - } - // se lo scambio fa risparmiare - if ( nTEST < nEXIST) { - // inverto il tratto intermedio - for ( NODE* pCurr = pFirst ; pCurr != pKth ;) { - NODE* pSave = pCurr->pNext ; - pCurr->pNext = pCurr->pPrev ; - pCurr->pPrev = pSave ; - pCurr = pSave ; + // se non esistono dipendenze suggerite + if ( ! ExistSuggDependences()) { + // lunghezza originale dei tratti da scambiare + unsigned nEXIST = ArcCost( pLast->nPos, pFirst->nPos) + + ArcCost( pKth->nPos, pKth->pNext->nPos) ; + // nuova lunghezza dei tratti da scambiare + unsigned nTEST = ArcCost( pFirst->nPos, pKth->pNext->nPos) + + ArcCost( pLast->nPos, pKth->nPos) ; + // lunghezze originali e nuova del tratto intermedio che viene invertito + for ( NODE* pCurr = pFirst ; pCurr != pKth ; pCurr = pCurr->pNext) { + nEXIST += ArcCost( pCurr->nPos, pCurr->pNext->nPos) ; + nTEST += ArcCost( pCurr->pNext->nPos, pCurr->nPos) ; } - // sistemo gli estremi - pFirst->pNext = pKth->pNext ; - pKth->pNext->pPrev = pFirst ; - pKth->pNext = pKth->pPrev ; - pKth->pPrev = pLast ; - pLast->pNext = pKth ; - pFirst = pLast->pNext ; - pKth = pFirst->pNext ; - // faccio ripartire il conto - nCount = 0 ; + // se lo scambio fa risparmiare + if ( nTEST < nEXIST) { + // se non esistono vincoli, effettuo i cambi + if ( ! ExistConstraints()) { + SwapNodesForTwoOpt( pFirst, pKth, pLast) ; + nCount = 0 ; + } + // se esistono i vincoli + else { + // copio il Path e i puntatori sul Path di test per le dipendenze + CopyPathAdv( pPath, m_pCheckDep, pLast, pFirst, pKth, &pLastDep, &pFirstDep, &pKthDep) ; + // effettuo i cambi dei nodi sul path di test per le dipendenze + SwapNodesForTwoOpt( pFirstDep, pKthDep, pLastDep) ; + // se il path di test rispetta i vincoli, allora effettuo i cambi sul percorso vero + if ( IsPathRespectingContraints( m_pCheckDep)) { + SwapNodesForTwoOpt( pFirst, pKth, pLast) ; + nCount = 0 ; + } + // altrimenti passo al nodo successivo + else + pKth = pKth->pNext ; + } + } + // altrimenti passo al nodo successivo + else + pKth = pKth->pNext ; + } + // se esistono dipendenze suggerite + else { + CopyPathAdv( pPath, m_pCheckDep, pLast, pFirst, pKth, &pLastDep, &pFirstDep, &pKthDep) ; + SwapNodesForTwoOpt( pFirstDep, pKthDep, pLastDep) ; + if ( IsPathRespectingContraints( m_pCheckDep)) { + unsigned long long nEXIST = TotalCost( pPath) ; + unsigned long long nTEST = TotalCost( m_pCheckDep) ; + if ( nTEST < nEXIST) { + SwapNodesForTwoOpt( pFirst, pKth, pLast) ; + nCount = 0 ; + } + // altrimenti passo al nodo successivo + else + pKth = pKth->pNext ; + } + // altrimenti passo al nodo successivo + else + pKth = pKth->pNext ; } - // altrimenti passo al nodo successivo - else - pKth = pKth->pNext ; } while ( pKth != pLast->pPrev->pPrev && nCount != 0) ; if ( nCount != 0) pFirst = pFirst->pNext ;