/// <summary> /// Obtém o valor da estimativa a ser adicionada a cada um dos nós durante o algoritmo. /// </summary> /// <param name="input">Os dados de entrada.</param> /// <param name="line">A linha correspondente ao nó de cobertura.</param> /// <param name="component">A matriz dos custos.</param> /// <returns>O valor da estimativa.</returns> private ElementType GetBigDelta( DualHeuristicAlgInput <ElementType> input, KeyValuePair <int, ILongSparseMatrixLine <ElementType> > line, SparseDictionaryMathMatrix <ElementType> component) { var result = this.ring.Add(input.Gamma, this.ring.AdditiveInverse(input.Taus[line.Key])); foreach (var otherLine in component.GetLines()) { if (otherLine.Key != line.Key) { if (otherLine.Value.ContainsColumn(line.Key)) { var currentCost = otherLine.Value[line.Key]; var currentLambda = input.Lambdas[line.Key]; if (this.comparer.Compare(currentLambda, currentCost) >= 0) { var difference = this.ring.Add( input.Gamma, this.ring.AdditiveInverse(input.Taus[otherLine.Key])); if (this.comparer.Compare(difference, result) < 0) { result = difference; } } } } } return(result); }
/// <summary> /// Obtém um vector com todos os elementos de uma linha, incluindo os valores por defeito. /// </summary> /// <typeparam name="T">O tipo de objectos que constituem as entradas da matriz.</typeparam> /// <param name="line">A linha.</param> /// <param name="matrix">A matriz.</param> /// <returns>O vector com as entradas correspondentes à linha da matriz especificada.</returns> private T[] GetLineAsArray <T>(int line, SparseDictionaryMathMatrix <T> matrix) { var matrixColumnsNumber = matrix.GetLength(1); var currentMatrixLine = default(ILongSparseMatrixLine <T>); var result = new T[matrixColumnsNumber]; if (matrix.TryGetLine(line, out currentMatrixLine)) { for (int i = 0; i < matrixColumnsNumber; ++i) { var currentValue = default(T); if (currentMatrixLine.TryGetColumnValue(i, out currentValue)) { result[i] = currentMatrixLine[i]; } else { result[i] = matrix.DefaultValue; } } } else { for (int i = 0; i < result.Length; ++i) { result[i] = matrix.DefaultValue; } } return(result); }
/// <summary> /// Constrói uma matriz de teste na qual o valor por defeito é nulo. /// </summary> /// <remarks> /// - 1 - 2 - /// - 3 - 4 - /// 1 2 - - 3 /// - - - - - /// 3 1 4 5 2 /// </remarks> /// <param name="defaultValue">O valor por defeito.</param> /// <returns>A matriz de teste.</returns> private SparseDictionaryMathMatrix <Fraction <int> > GetDefaultMatrix(Fraction <int> defaultValue) { var result = new SparseDictionaryMathMatrix <Fraction <int> >(5, 5, defaultValue); var integerDomain = new IntegerDomain(); // Primeira linha. result[0, 1] = new Fraction <int>(1, 1, integerDomain); result[0, 3] = new Fraction <int>(1, 1, integerDomain); // Segunda linha. result[1, 1] = new Fraction <int>(3, 1, integerDomain); result[1, 3] = new Fraction <int>(4, 1, integerDomain); // Terceira linha. result[2, 0] = new Fraction <int>(1, 1, integerDomain); result[2, 1] = new Fraction <int>(2, 1, integerDomain); result[2, 4] = new Fraction <int>(3, 1, integerDomain); // Quinta linha. result[4, 0] = new Fraction <int>(3, 1, integerDomain); result[4, 1] = new Fraction <int>(1, 1, integerDomain); result[4, 2] = new Fraction <int>(4, 1, integerDomain); result[4, 3] = new Fraction <int>(5, 1, integerDomain); result[4, 4] = new Fraction <int>(2, 1, integerDomain); return(result); }
private ElementType GetMinimumSum( IntegerSequence chosenReferences, SparseDictionaryMathMatrix <ElementType> currentMatrix, ElementType[] currentLineBoard, IEnumerable <KeyValuePair <int, ILongSparseMatrixLine <ElementType> > > lines, int chosenSolution) { var sum = this.ring.AdditiveUnity; var solutionValue = chosenReferences[chosenSolution]; var minimumCover = GetMinimumCover(chosenReferences, lines, solutionValue); sum = this.ring.Add(sum, minimumCover); var columns = currentMatrix.GetColumns(solutionValue); foreach (var column in columns) { if (column.Key != solutionValue) { var currentValue = currentMatrix[solutionValue, column.Key]; if (this.ring.Equals(currentValue, currentLineBoard[column.Key])) { minimumCover = this.GetMinimumCover( chosenReferences, lines, column.Key); minimumCover = this.ring.Add(minimumCover, this.ring.AdditiveInverse(currentValue)); sum = this.ring.Add(sum, minimumCover); } } } return(sum); }
/// <summary> /// Obtém o custo inicial da matriz especificada. /// </summary> /// <param name="matrix">A matriz.</param> /// <returns>O custo.</returns> private ElementType GetMatrixInitialCost(SparseDictionaryMathMatrix <ElementType> matrix) { var result = this.ring.AdditiveUnity; if (matrix.GetLength(0) > 0) { var firstLine = matrix[0]; var coverColumns = firstLine.GetColumns(); foreach (var column in coverColumns) { result = this.ring.Add(result, column.Value); } } return(result); }
/// <summary> /// Verifica a validade da combinação de uma linha com a próxima tendo em conta os factores proporcionados. /// </summary> /// <typeparam name="T">O tipo de objectos que constituem as entradas das matrizes.</typeparam> /// <param name="target">A matriz.</param> /// <param name="firstFactor">O factor afecto à linha a ser substituída.</param> /// <param name="secondFactor">O factor afecto à linha a ser combinada.</param> /// <param name="ring">O anel responsável pelas operações sobre os coeficientes.</param> private void AssertTarget <T>( SparseDictionaryMathMatrix <T> target, T firstFactor, T secondFactor, IRing <T> ring) { var numberOfLines = target.GetLength(0); var numberOfColumns = target.GetLength(1); var firstLine = 0; var secondLine = 1; while (secondLine < numberOfLines) { var firstArray = this.GetLineAsArray(firstLine, target); var secondArray = this.GetLineAsArray(secondLine, target); this.CombineArrays(firstArray, secondArray, firstFactor, secondFactor, ring); target.CombineLines(firstLine, secondLine, firstFactor, secondFactor, ring); var targetLine = default(ILongSparseMatrixLine <T>); if (target.TryGetLine(firstLine, out targetLine)) { for (int i = 0; i < numberOfColumns; ++i) { var targetValue = default(T); if (!targetLine.TryGetColumnValue(i, out targetValue)) { targetValue = target.DefaultValue; } Assert.AreEqual(firstArray[i], targetValue); } } else { for (int i = 0; i < numberOfColumns; ++i) { Assert.AreEqual(firstArray[i], target.DefaultValue); } } ++firstLine; ++secondLine; } }
/// <summary> /// Verifica a validade de todas as linhas com excepção da linha especificada. /// </summary> /// <param name="line">A linha a ser ignorada.</param> /// <param name="matrix">A matriz a ser verificada.</param> private void AssertLinesExcept(int line, SparseDictionaryMathMatrix <Fraction <int> > matrix) { var lineDimension = matrix.GetLength(0); var columnDimension = matrix.GetLength(1); var assertMatrix = this.GetDefaultMatrix(matrix.DefaultValue); for (int i = 0; i < line; ++i) { for (int j = 0; j < columnDimension; ++j) { Assert.AreEqual(assertMatrix[i, j], matrix[i, j]); } } for (int i = line + 1; i < lineDimension; ++i) { for (int j = 0; j < columnDimension; ++j) { Assert.AreEqual(assertMatrix[i, j], matrix[i, j]); } } }
/// <summary> /// Aplica o algoritmo dual aos dados de entrada para um determinado número de referências. /// </summary> /// <remarks> /// Os dados de entrada são alterados pelo algoritmo e podem ser reutilizados em fases posteriores. /// O valor de gama tem de ser previamente estimado. /// </remarks> /// <param name="refsNumber">O número de referências.</param> /// <param name="matrix">A matriz com os custos.</param> /// <param name="input">Os dados de entrada.</param> /// <returns>O custo aproximado pela heurística.</returns> public ElementType Run( int refsNumber, SparseDictionaryMathMatrix <ElementType> matrix, DualHeuristicAlgInput <ElementType> input) { if (refsNumber < 1) { throw new ArgumentException("The number of references must be at least one."); } else if (matrix == null) { throw new ArgumentNullException("matrix"); } else if (input == null) { throw new ArgumentNullException("input"); } else { this.dualGammaEstimAlgorithm.Run(matrix, input); return(this.Process(refsNumber, matrix, input)); } }
/// <summary> /// Obtém uma solução corrigida a partir de uma inicial. /// </summary> /// <param name="refsNumber">O número de referências.</param> /// <param name="matrix">A matriz.</param> /// <param name="initialSolution">A solução inicial.</param> /// <param name="initialLineBoard">A linha condensada inicial.</param> /// <returns> /// A solução obtida. /// </returns> public GreedyAlgSolution <ElementType> Run( int refsNumber, SparseDictionaryMathMatrix <ElementType> matrix, GreedyAlgSolution <ElementType> initialSolution, ElementType[] initialLineBoard) { if (refsNumber < 1) { throw new ArgumentException("The number of references must be at least one."); } else if (matrix == null) { throw new ArgumentNullException("matrix"); } else if (initialSolution == null) { throw new ArgumentNullException("initialSolution"); } else if (initialLineBoard == null) { throw new ArgumentNullException("initialLineBoard"); } else { var greedyAlgResult = initialSolution.Clone(); var initialRefsNumber = greedyAlgResult.Chosen.Count; if (refsNumber < initialRefsNumber) { var refGetAlg = this.inverseRefGetAlgorithm; while (refsNumber < initialRefsNumber) { var nextRef = refGetAlg.Run(greedyAlgResult.Chosen, matrix, initialLineBoard); if (nextRef.Item1 == -1) { return(null); } else { greedyAlgResult.Chosen.Remove(greedyAlgResult.Chosen[nextRef.Item1]); greedyAlgResult.Cost = this.ring.Add(greedyAlgResult.Cost, nextRef.Item2); } } } else if (refsNumber > initialRefsNumber) { var refGetAlg = this.directRefGetAlgorithm; var nextRef = refGetAlg.Run(greedyAlgResult.Chosen, matrix, initialLineBoard); if (nextRef.Item1 == -1) { return(null); } else { greedyAlgResult.Chosen.Remove(greedyAlgResult.Chosen[nextRef.Item1]); greedyAlgResult.Cost = this.ring.Add( greedyAlgResult.Cost, this.ring.AdditiveInverse(nextRef.Item2)); } } return(greedyAlgResult); } }
/// <summary> /// Obtém os valores iniciais das variáveis lambda. /// </summary> /// <param name="matrix">A matriz dos custos.</param> /// <param name="greedySolution">A solução do algoritmo guloso.</param> /// <param name="dualInput">A entrada para o algoritmo dual propriamente dito.</param> /// <returns>Verdadeiro caso o algoritmo seja bem sucedido e falso caso contrário.</returns> public bool Run( SparseDictionaryMathMatrix <ElementType> matrix, GreedyAlgSolution <ElementType> greedySolution, DualHeuristicAlgInput <ElementType> dualInput) { if (matrix == null) { throw new ArgumentNullException("matrix"); } else if (greedySolution == null) { throw new ArgumentNullException("greedySolution"); } else if (dualInput == null) { throw new ArgumentNullException("dualInput"); } else { dualInput.Lambdas.Clear(); var componentEnum = matrix.GetLines().GetEnumerator(); if (componentEnum.MoveNext()) { dualInput.Lambdas.Add(componentEnum.Current.Key, this.ring.AdditiveUnity); foreach (var column in componentEnum.Current.Value.GetColumns()) { if (column.Key != componentEnum.Current.Key) { if (greedySolution.Chosen.Contains(column.Key)) { dualInput.Lambdas.Add(column.Key, this.ring.AdditiveUnity); } else { dualInput.Lambdas.Add(column.Key, column.Value); } } } while (componentEnum.MoveNext()) { if (greedySolution.Chosen.Contains(componentEnum.Current.Key)) { foreach (var column in componentEnum.Current.Value.GetColumns()) { if (column.Key != componentEnum.Current.Key) { var forComponentValue = this.ring.AdditiveUnity; if (dualInput.Lambdas.TryGetValue(column.Key, out forComponentValue)) { if (this.comparer.Compare(column.Value, forComponentValue) < 0) { dualInput.Lambdas[column.Key] = column.Value; } } else { dualInput.Lambdas.Add(column.Key, this.ring.AdditiveUnity); } } } } } } return(true); } }
/// <summary> /// Aplica o algoritmo sobre a matriz dos custos. /// </summary> /// <param name="costs">A matriz dos custos.</param> /// <param name="numberOfMedians">O número de medianas a serem escolhidas.</param> /// <returns>O resultado da relaxação.</returns> public CoeffType[] Run(SparseDictionaryMathMatrix <CoeffType> costs, int numberOfMedians) { if (costs == null) { throw new ArgumentNullException("costs"); } else { var numberOfVertices = costs.GetLength(1); var objectiveFunction = new List <SimplexMaximumNumberField <CoeffType> >(); // A mediana que cobre todas as outras é ligeiramente diferente objectiveFunction.Add(new SimplexMaximumNumberField <CoeffType>( this.coeffsField.AdditiveUnity, this.coeffsField.AdditiveInverse(this.coeffsField.MultiplicativeUnity))); for (int i = 1; i < numberOfVertices; ++i) { objectiveFunction.Add(new SimplexMaximumNumberField <CoeffType>( this.coeffsField.AdditiveUnity, this.coeffsField.AddRepeated( this.coeffsField.AdditiveInverse(this.coeffsField.MultiplicativeUnity), 2))); } // Adiciona as variáveis x à função objectivo. var numberXVars = 0; foreach (var line in costs.GetLines()) { foreach (var column in line.Value.GetColumns()) { if (line.Key != column.Key) { ++numberXVars; var valueToAdd = new SimplexMaximumNumberField <CoeffType>( column.Value, this.coeffsField.AdditiveInverse(this.coeffsField.MultiplicativeUnity)); objectiveFunction.Add(valueToAdd); } } } // Cria a matriz das restrições e preenche-a com os valores correctos var constraintsNumber = numberXVars + numberOfVertices; var nonBasicVariables = new int[objectiveFunction.Count]; var basicVariables = new int[constraintsNumber]; var linesLength = nonBasicVariables.Length + basicVariables.Length; // Preence os vectores que permitem seleccionar as variáveis. this.FillVariablesSelectors(nonBasicVariables, basicVariables, linesLength); // Preencimento da matriz das restrições var constraintsMatrix = new ArrayMathMatrix <CoeffType>( constraintsNumber, constraintsNumber, this.coeffsField.AdditiveUnity); var constraintLineNumber = 0; var constraintXYLineNumber = 0; var unity = this.coeffsField.MultiplicativeUnity; var inverseAdditive = this.coeffsField.AdditiveInverse(unity); foreach (var line in costs.GetLines()) { foreach (var column in line.Value.GetColumns()) { if (line.Key != column.Key) { constraintsMatrix[constraintXYLineNumber, constraintLineNumber] = inverseAdditive; constraintsMatrix[constraintXYLineNumber, constraintXYLineNumber + numberOfVertices] = unity; ++constraintXYLineNumber; } } ++constraintLineNumber; } var lastLine = constraintsNumber - 1; constraintsMatrix[lastLine, 0] = unity; constraintLineNumber = numberXVars; for (int i = 1; i < numberOfVertices; ++i) { constraintsMatrix[constraintLineNumber, i] = unity; constraintsMatrix[lastLine, i] = unity; constraintXYLineNumber = numberOfVertices; foreach (var lines in costs.GetLines()) { foreach (var column in lines.Value.GetColumns()) { if (lines.Key != column.Key) { if (column.Key == i) { constraintsMatrix[constraintLineNumber, constraintXYLineNumber] = unity; } ++constraintXYLineNumber; } } } ++constraintLineNumber; } // Preenchimento do vector independente das restrições var vector = new ArrayMathVector <CoeffType>(constraintsNumber, this.coeffsField.AdditiveUnity); lastLine = constraintsNumber - 1; for (int i = numberXVars; i < lastLine; ++i) { vector[i] = unity; } vector[lastLine] = this.conversion.InverseConversion(numberOfMedians); var sumVector = this.coeffsField.AdditiveUnity; for (int i = 0; i < vector.Length; ++i) { sumVector = this.coeffsField.Add(sumVector, vector[i]); } sumVector = this.coeffsField.AdditiveInverse(sumVector); var simplexInput = new SimplexInput <CoeffType, SimplexMaximumNumberField <CoeffType> >( basicVariables, nonBasicVariables, new ArrayMathVector <SimplexMaximumNumberField <CoeffType> >(objectiveFunction.ToArray()), new SimplexMaximumNumberField <CoeffType>(this.coeffsField.AdditiveUnity, sumVector), constraintsMatrix, vector); var resultSimplexSol = this.simplexAlg.Run(simplexInput); var result = new CoeffType[numberOfVertices]; Array.Copy(resultSimplexSol.Solution, result, numberOfVertices); return(result); } }
/// <summary> /// Realiza as iterações de melhoramento sobre os dados aproximados inicialmente. /// </summary> /// <param name="refsNumber">O número de referências.</param> /// <param name="matrix">A matriz dos custos.</param> /// <param name="input">Os dados de entrada.</param> /// <returns>O valor do custo dual.</returns> private ElementType Process( int refsNumber, SparseDictionaryMathMatrix <ElementType> matrix, DualHeuristicAlgInput <ElementType> input) { var multiplicativeSymmetric = this.ring.AdditiveInverse(this.ring.MultiplicativeUnity); var delta = this.ring.AdditiveUnity; while (!this.ring.IsMultiplicativeUnity(delta)) { foreach (var line in matrix.GetLines()) { var bigDelta = this.GetBigDelta( input, line, matrix); var currentLambda = input.Lambdas[line.Key]; if (input.Cbar.ContainsKey(line.Key)) { var value = this.ring.Add( input.Cbar[line.Key], this.ring.AdditiveInverse(currentLambda)); if (this.comparer.Compare(value, bigDelta) < 0) { bigDelta = value; delta = this.ring.MultiplicativeUnity; input.Cbar.Remove(line.Key); // TODO: cbar = min(u que cobre line tal que o custo é maior que lambda[line] foreach (var coverLine in matrix.GetLines()) { if (coverLine.Key != line.Key) { if (coverLine.Value.ContainsColumn(line.Key)) { var currentCost = coverLine.Value[line.Key]; var compareCost = this.ring.Add(currentLambda, bigDelta); if (this.comparer.Compare(compareCost, currentCost) < 0) { var currentCbar = this.ring.AdditiveUnity; if (input.Cbar.TryGetValue(line.Key, out currentCbar)) { if (this.comparer.Compare(currentCost, currentCbar) < 0) { input.Cbar[line.Key] = currentCost; } } else { input.Cbar.Add(line.Key, currentCost); } } } } } } } if (this.comparer.Compare(this.ring.AdditiveUnity, bigDelta) < 0) { foreach (var coveringLine in matrix.GetLines()) { if (coveringLine.Key != line.Key) { if (coveringLine.Value.ContainsColumn(line.Key)) { if (this.comparer.Compare(coveringLine.Value[line.Key], currentLambda) <= 0) { input.Taus[coveringLine.Key] = this.ring.Add( input.Taus[coveringLine.Key], bigDelta); } } } } input.Lambdas[line.Key] = this.ring.Add(input.Lambdas[line.Key], bigDelta); input.Taus[line.Key] = this.ring.Add(input.Taus[line.Key], bigDelta); } } } var result = this.ring.AdditiveUnity; var prod = this.ring.AddRepeated(input.Gamma, refsNumber); foreach (var line in matrix.GetLines()) { result = this.ring.Add(result, input.Lambdas[line.Key]); } result = this.ring.Add(result, this.ring.AdditiveInverse(prod)); return(result); }
public ElementType Run( SparseDictionaryMathMatrix <ElementType> matrix, DualHeuristicAlgInput <ElementType> input) { if (matrix == null) { throw new ArgumentNullException("matrix"); } else if (input == null) { throw new ArgumentNullException("input"); } else { input.Taus.Clear(); var result = new Dictionary <int, ElementType>(); input.Gamma = this.ring.AdditiveUnity; var currentGamma = this.ring.AdditiveUnity; foreach (var line in matrix.GetLines()) { // Setup the tau and cbar values var tempTau = input.Lambdas[line.Key]; foreach (var coveredLine in line.Value.GetColumns()) { if (coveredLine.Key != line.Key) { var difference = this.ring.Add( input.Lambdas[coveredLine.Key], this.ring.AdditiveInverse(coveredLine.Value)); if (this.comparer.Compare(difference, this.ring.AdditiveUnity) < 0) { tempTau = this.ring.Add(tempTau, difference); } } } if (this.comparer.Compare(input.Gamma, tempTau) < 0) { input.Gamma = tempTau; } if (this.comparer.Compare(currentGamma, tempTau) < 0) { currentGamma = tempTau; } input.Taus.Add(line.Key, tempTau); } foreach (var line in matrix.GetLines()) { foreach (var coveredLine in line.Value.GetColumns()) { if (coveredLine.Key != line.Key) { var tempCbarValue = this.ring.Add( coveredLine.Value, this.ring.AdditiveInverse(input.Lambdas[coveredLine.Key])); if (this.comparer.Compare(this.ring.AdditiveUnity, tempCbarValue) < 0) { var currentCbarInCoveredVertice = this.ring.AdditiveUnity; if (input.Cbar.TryGetValue(coveredLine.Key, out currentCbarInCoveredVertice)) { if (this.comparer.Compare(tempCbarValue, currentCbarInCoveredVertice) < 0) { input.Cbar[coveredLine.Key] = coveredLine.Value; } } else { input.Cbar.Add(coveredLine.Key, coveredLine.Value); } } } } } return(currentGamma); } }
/// <summary> /// Escolhe a referência que minimiza a perda. /// </summary> /// <param name="chosenReferences">As referências escolhidas anteriormente.</param> /// <param name="currentMatrix">A matriz dos custos.</param> /// <param name="currentLineBoard">A linha que contém a condensação dos custos das linhas escolhidas.</param> /// <returns>O índice da linha correspondente à próxima referência bem como a perda respectiva.</returns> public Tuple <int, ElementType> Run( IntegerSequence chosenReferences, SparseDictionaryMathMatrix <ElementType> currentMatrix, ElementType[] currentLineBoard) { var result = -1; var currentMinimumLost = default(ElementType); var lines = currentMatrix.GetLines(); if (chosenReferences.Count > 2) { currentMinimumLost = this.GetMinimumSum( chosenReferences, currentMatrix, currentLineBoard, lines, 2); result = 2; } Parallel.For(2, chosenReferences.Count, chosenSolution => { var sum = this.GetMinimumSum( chosenReferences, currentMatrix, currentLineBoard, lines, chosenSolution); lock (this.lockObject) { if (this.comparer.Compare(sum, currentMinimumLost) < 0) { currentMinimumLost = sum; result = chosenSolution; } } }); if (result != -1) { var solutionValue = chosenReferences[result]; var minimumCover = this.GetMinimumCover( chosenReferences, lines, solutionValue); currentLineBoard[solutionValue] = minimumCover; var columns = currentMatrix.GetColumns(solutionValue); foreach (var column in columns) { if (column.Key != solutionValue) { var currentValue = currentMatrix[solutionValue, column.Key]; if (this.ring.Equals(currentValue, currentLineBoard[column.Key])) { minimumCover = this.GetMinimumCover( chosenReferences, lines, solutionValue); currentLineBoard[column.Key] = minimumCover; } } } } return(Tuple.Create(result, currentMinimumLost)); }