diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bd6c70..ef1009b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,3 +9,40 @@ All notable changes to this project will be documented in this file. See [versio * add vector normalization ([c819676](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/c81967667e032413cdad426a9a5a0e6dfecd6804)) + +## [2.0.1](https://www.github.com/SarcasticMoose/mcda-toolkit/releases/tag/v2.0.1) (2024-06-12) + +### Bug Fixes + +* remove redundant casting ([6db1dbc](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/6db1dbc1f88f2e342d8b3d7aa81364d90e4dfab5)) +* update broken project ([ea46d0d](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/ea46d0d1c43ba0c25c957fbc6dd60e70dfd88326)) + + +## [2.0.0](https://www.github.com/SarcasticMoose/mcda-toolkit/releases/tag/v2.0.0) (2024-06-12) + +### Features + +* add enumeration iteration with index ([4c76513](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/4c76513a9100a629accd43f28bd41d33d6e211ea)) +* add generic methods ([7cfd3ea](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/7cfd3eac2afcd59b4c7f2e808eddf9aa926386a5)) +* add versioning ([8db0fc3](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/8db0fc30cd345134a7210b069fb519b193f27bc8)) + +### Bug Fixes + +* fix project ([a9cdc05](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/a9cdc05d72d01901eb0be13aa1772187fabfa083)) + +### Breaking Changes + +* adjust styling,names etc. ([d23c29d](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/d23c29dd9d1f3924f5aabc82b5a3472bc085d9dd)) + + +## [1.0.1](https://www.github.com/SarcasticMoose/mcda-toolkit/releases/tag/v1.0.1) (2024-06-03) + + +## [1.0.0](https://www.github.com/SarcasticMoose/mcda-toolkit/releases/tag/v1.0.0) (2024-06-03) + +### Features + +* add enumeration iteration with index ([5196aa1](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/5196aa1df9c2b86d6a495454b2e3fb9a2a5f3d1d)) +* add generic methods ([d75c45e](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/d75c45e9bb11a8a001be2151ffd8a070d1461fc7)) +* add versioning ([69b93d0](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/69b93d0b2d4669bb7ffb28f3e36fb754280ea352)) + diff --git a/McdaMethods.sln b/McdaMethods.sln index 85f515f..bf99a35 100644 --- a/McdaMethods.sln +++ b/McdaMethods.sln @@ -1,11 +1,10 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34525.116 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "McdaToolkit", "McdaToolkit\McdaToolkit.csproj", "{EC147B8B-336E-4744-8F54-43D1BB0E4FC3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "McdaToolkit", "src\McdaToolkit\McdaToolkit.csproj", "{EC2ACBF0-59C4-4B03-B9AD-54E31E69A791}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "McdaToolkit.UnitTests", "McdaToolkit.UnitTests\McdaToolkit.UnitTests.csproj", "{675DE565-59A0-4865-A306-B12FCC64EF1F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "McdaToolkit.UnitTests", "tests\McdaToolkit.UnitTests\McdaToolkit.UnitTests.csproj", "{C16A9430-0ABA-44D5-B9D2-256DEA133D38}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +12,14 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EC147B8B-336E-4744-8F54-43D1BB0E4FC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC147B8B-336E-4744-8F54-43D1BB0E4FC3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC147B8B-336E-4744-8F54-43D1BB0E4FC3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC147B8B-336E-4744-8F54-43D1BB0E4FC3}.Release|Any CPU.Build.0 = Release|Any CPU - {675DE565-59A0-4865-A306-B12FCC64EF1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {675DE565-59A0-4865-A306-B12FCC64EF1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {675DE565-59A0-4865-A306-B12FCC64EF1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {675DE565-59A0-4865-A306-B12FCC64EF1F}.Release|Any CPU.Build.0 = Release|Any CPU + {EC2ACBF0-59C4-4B03-B9AD-54E31E69A791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC2ACBF0-59C4-4B03-B9AD-54E31E69A791}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC2ACBF0-59C4-4B03-B9AD-54E31E69A791}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC2ACBF0-59C4-4B03-B9AD-54E31E69A791}.Release|Any CPU.Build.0 = Release|Any CPU + {C16A9430-0ABA-44D5-B9D2-256DEA133D38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C16A9430-0ABA-44D5-B9D2-256DEA133D38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C16A9430-0ABA-44D5-B9D2-256DEA133D38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C16A9430-0ABA-44D5-B9D2-256DEA133D38}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/McdaToolkit.UnitTests/McdaMethodsTests.cs b/McdaToolkit.UnitTests/McdaMethodsTests.cs deleted file mode 100644 index cb50ca9..0000000 --- a/McdaToolkit.UnitTests/McdaMethodsTests.cs +++ /dev/null @@ -1,147 +0,0 @@ -using FluentAssertions; -using MathNet.Numerics; -using McdaToolkit.McdaMethods; -using McdaToolkit.McdaMethods.Errors; -using McdaToolkit.UnitTests.Helpers; -using Xunit.Abstractions; - -namespace McdaToolkit.UnitTests; - -public class McdaMethodsTests -{ - private readonly ITestOutputHelper _testOutputHelper; - - public McdaMethodsTests(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } - - [Fact] - public void Calculate_TopsisMethod_ShouldBeEqualToExpected() - { - var matrix = new double[,] - { - { 66, 56, 95 }, - { 61, 55, 166 }, - { 65, 49, 113 }, - { 95, 56, 99 }, - { 63, 43, 178 }, - { 74, 59, 140 }, - }; - double[] weights = new double[] - { - 0.4,0.25,0.35 - }; - int[] types = new int[] - { - -1, - -1, - 1 - }; - double[] expectedTopsisScore = new double[] - { - 0.38805147,0.76189759,0.58509479,0.06374247,0.97647059,0.43681786 - }; - - var topsis = new TopsisMethod(); - var topsisResult = topsis.Calculate(matrix,weights,types); - - var final = topsisResult.Value; - - final.Enumerate() - .Select(x => x.Round(8)) - .Should() - .BeEquivalentTo(expectedTopsisScore); - } - - [Fact] - public void Calculate_WeightAreNotEqualOne_ShouldReturnResultFail() - { - var matrix = new double[,] - { - { 66, 56, 95 }, - { 61, 55, 166 }, - { 65, 49, 113 }, - { 95, 56, 99 }, - { 63, 43, 178 }, - { 74, 59, 140 }, - }; - double[] weights = new double[] - { - 0.8,0.25,0.35 - }; - int[] types = new int[] - { - -1, - -1, - 1 - }; - - var topsis = new TopsisMethod(); - var topsisResult = topsis.Calculate(matrix, weights, types); - - topsisResult.IsSuccess.Should().BeFalse(); - topsisResult.HasError(); - } - - [Fact] - public void Calculate_DecisionCriteriaAreNotBetweenMinusOneAndOne_ShouldReturnResultFail() - { - var matrix = new double[,] - { - { 66, 56, 95 }, - { 61, 55, 166 }, - { 65, 49, 113 }, - { 95, 56, 99 }, - { 63, 43, 178 }, - { 74, 59, 140 }, - }; - double[] weights = new double[] - { - 0.4,0.25,0.35 - }; - int[] types = new int[] - { - -1, - -2, - 1 - }; - - var topsis = new TopsisMethod(); - var topsisResult = topsis.Calculate(matrix, weights, types); - - topsisResult.IsSuccess.Should().BeFalse(); - topsisResult.HasError(); - } - - [Fact] - public void Calculate_DimensionsOfAllMatrixesNotTheSame_ShouldReturnResultFail() - { - var matrix = new double[,] - { - { 66, 56, 95 }, - { 61, 55, 166 }, - { 65, 49, 113 }, - { 95, 56, 99 }, - { 63, 43, 178 }, - { 74, 59, 140 }, - }; - double[] weights = new double[] - { - 0.4,0.25,0.15,0.20 - }; - int[] types = new int[] - { - -1, - -1, - 1, - 1 - }; - - var topsis = new TopsisMethod(); - var topsisResult = topsis.Calculate(matrix, weights, types); - - topsisResult.IsSuccess.Should().BeFalse(); - topsisResult.HasError(); - } -} \ No newline at end of file diff --git a/McdaToolkit.UnitTests/NormalizationUnitTests.cs b/McdaToolkit.UnitTests/NormalizationUnitTests.cs deleted file mode 100644 index 007765a..0000000 --- a/McdaToolkit.UnitTests/NormalizationUnitTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -using FluentAssertions; -using MathNet.Numerics.LinearAlgebra; -using McdaToolkit.Enums; -using McdaToolkit.Normalization; -using McdaToolkit.UnitTests.Helpers; - -namespace McdaToolkit.UnitTests; - -public class NormalizationUnitTests -{ - private readonly Matrix _matrixToNormalize = Matrix.Build.DenseOfArray(new double[,] - { - { 32.57, 14.56, 87.12, 56.34, 47.89 }, - { 93.23, 76.34, 33.78, 25.68, 64.23 }, - { 78.45, 68.92, 45.67, 87.34, 39.45 }, - { 54.12, 34.56, 56.89, 92.56, 81.56 }, - { 21.76, 53.23, 73.21, 65.78, 57.34 }, - { 85.34, 98.45, 38.45, 23.45, 62.89 }, - { 42.68, 27.34, 49.67, 74.12, 53.21 } - }); - - private readonly int[] _types = [-1, -1, 1, 1, -1]; - - [Fact] - public void Normalize_MinMaxNormalization_ShouldReturnedExpectedValues() - { - var expected = new double[][] - { - [0.84874773, 1, 1, 0.47590797, 0.79957255], - [0, 0.26355942, 0, 0.03226740, 0.41154120], - [0.20680006, 0.35200858, 0.22290964, 0.92446824, 1], - [0.54722261, 0.76159256, 0.43325834, 1, 0], - [1, 0.53903922, 0.73922010, 0.61250181, 0.57516029], - [0.11039597, 0, 0.08755156, 0, 0.44336262], - [0.70728977, 0.84765765, 0.29790026, 0.73317899, 0.67323676] - }; - var dataNormalization = new DataNormalizationService(NormalizationMethodEnum.MinMax); - - var normalizedMatrix = dataNormalization.NormalizeMatrix(_matrixToNormalize,_types); - - var equalityResult = TestHelpers.CheckEquality(normalizedMatrix, expected); - equalityResult.Should().BeTrue(); - } - - [Fact] - public void Normalize_VectorNormalization_ShouldReturnedExpectedValues() - { - var normalizationType = NormalizationMethodEnum.Vector; - var expected = new double[][] - { - [0.80678026, 0.90838501, 0.57002794, 0.32313221, 0.69529318], - [0.44691814, 0.51965053, 0.22102323, 0.14728497, 0.59132764], - [0.53459968, 0.56633894, 0.29881974, 0.50092948, 0.74899386], - [0.67893607, 0.78254024, 0.37223243, 0.53086825, 0.48106309], - [0.87090999, 0.66506416, 0.47901452, 0.37727434, 0.63516623], - [0.49372513, 0.38052914, 0.25157913, 0.13449503, 0.59985358], - [0.74680324, 0.82797021, 0.32499182, 0.42510755, 0.66144393] - }; - var dataNormalization = new DataNormalizationService(normalizationType); - - var normalizedMatrix = dataNormalization.NormalizeMatrix(_matrixToNormalize,_types); - - var equalityResult = TestHelpers.CheckEquality(normalizedMatrix, expected); - equalityResult.Should().BeTrue(); - } -} \ No newline at end of file diff --git a/McdaToolkit/CHANGELOG.md b/McdaToolkit/CHANGELOG.md deleted file mode 100644 index 9531e4d..0000000 --- a/McdaToolkit/CHANGELOG.md +++ /dev/null @@ -1,41 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. See [versionize](https://github.com/versionize/versionize) for commit guidelines. - - -## [2.0.1](https://www.github.com/SarcasticMoose/mcda-toolkit/releases/tag/v2.0.1) (2024-06-12) - -### Bug Fixes - -* remove redundant casting ([6db1dbc](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/6db1dbc1f88f2e342d8b3d7aa81364d90e4dfab5)) -* update broken project ([ea46d0d](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/ea46d0d1c43ba0c25c957fbc6dd60e70dfd88326)) - - -## [2.0.0](https://www.github.com/SarcasticMoose/mcda-toolkit/releases/tag/v2.0.0) (2024-06-12) - -### Features - -* add enumeration iteration with index ([4c76513](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/4c76513a9100a629accd43f28bd41d33d6e211ea)) -* add generic methods ([7cfd3ea](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/7cfd3eac2afcd59b4c7f2e808eddf9aa926386a5)) -* add versioning ([8db0fc3](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/8db0fc30cd345134a7210b069fb519b193f27bc8)) - -### Bug Fixes - -* fix project ([a9cdc05](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/a9cdc05d72d01901eb0be13aa1772187fabfa083)) - -### Breaking Changes - -* adjust styling,names etc. ([d23c29d](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/d23c29dd9d1f3924f5aabc82b5a3472bc085d9dd)) - - -## [1.0.1](https://www.github.com/SarcasticMoose/mcda-toolkit/releases/tag/v1.0.1) (2024-06-03) - - -## [1.0.0](https://www.github.com/SarcasticMoose/mcda-toolkit/releases/tag/v1.0.0) (2024-06-03) - -### Features - -* add enumeration iteration with index ([5196aa1](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/5196aa1df9c2b86d6a495454b2e3fb9a2a5f3d1d)) -* add generic methods ([d75c45e](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/d75c45e9bb11a8a001be2151ffd8a070d1461fc7)) -* add versioning ([69b93d0](https://www.github.com/SarcasticMoose/mcda-toolkit/commit/69b93d0b2d4669bb7ffb28f3e36fb754280ea352)) - diff --git a/McdaToolkit/Enums/NormalizationMethodEnum.cs b/McdaToolkit/Enums/NormalizationMethodEnum.cs deleted file mode 100644 index fff4dc5..0000000 --- a/McdaToolkit/Enums/NormalizationMethodEnum.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace McdaToolkit.Enums; - -public enum NormalizationMethodEnum -{ - MinMax, - Vector -} \ No newline at end of file diff --git a/McdaToolkit/Extensions/EnumerableExtentions.cs b/McdaToolkit/Extensions/EnumerableExtentions.cs deleted file mode 100644 index 4d2848d..0000000 --- a/McdaToolkit/Extensions/EnumerableExtentions.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace McdaToolkit.Extensions; - -internal static class EnumerableExtentions -{ - public static IEnumerable<(T item, int index)> Indexed(this IEnumerable source) - { - if (source is null) - { - throw new ArgumentNullException(); - } - - var i = 0; - foreach (var item in source) - { - yield return (item, i); - ++i; - } - } -} \ No newline at end of file diff --git a/McdaToolkit/McdaMethods/Abstraction/McdaMethod.cs b/McdaToolkit/McdaMethods/Abstraction/McdaMethod.cs deleted file mode 100644 index 68d5cc7..0000000 --- a/McdaToolkit/McdaMethods/Abstraction/McdaMethod.cs +++ /dev/null @@ -1,59 +0,0 @@ -using LightResults; -using MathNet.Numerics.LinearAlgebra; -using McdaToolkit.McdaMethods.Errors; -using McdaToolkit.McdaMethods.Helpers; -using McdaToolkit.McdaMethods.Interfaces; - -namespace McdaToolkit.McdaMethods.Abstraction; - -public abstract class McdaMethod : IMethod -{ - private Result InitialErrorsCheck(double[,] matrix, double[] weights, int[] criteriaDirections) - { - if (weights == null) - { - throw new ArgumentNullException(nameof(weights), "Value cannot be null"); - } - - if (criteriaDirections == null) - { - throw new ArgumentNullException(nameof(criteriaDirections), "Value cannot be null"); - } - - var isWeightsCorrect = CheckDataHelper.IsWeightEqualOne(weights); - - if (!isWeightsCorrect) - { - return Result.Fail(new WeightNotSumToOneError()); - } - - var isCriteriaDecisionCorrect = CheckDataHelper.IsCriteriaDesisionBetweenMinusOneAndOne(criteriaDirections); - - if (!isCriteriaDecisionCorrect) - { - return Result.Fail(new CriteriaNotBetweenMinusOneAndOne()); - } - - var isSizesAreCorrect = CheckDataHelper.IsDataWeightsAndTypesHaveCorrectSizes(matrix, weights, criteriaDirections); - - if (!isSizesAreCorrect) - { - return Result.Fail(new ArraySizesAreNotEqual()); - } - - return Result.Ok(); - } - public Result> Calculate(double[,] matrix, double[] weights, int[] criteriaDirections) - { - var errorsCheckResult = InitialErrorsCheck(matrix,weights, criteriaDirections); - - if (errorsCheckResult.IsFailed) - { - return Result.Fail>(errorsCheckResult.Errors); - } - - var matrixTypeOfMatrix = Matrix.Build.DenseOfArray(matrix); - return Calculate(matrixTypeOfMatrix, weights, criteriaDirections); - } - protected abstract Result> Calculate(Matrix matrix, double[] weights, int[] criteriaDirections); -} \ No newline at end of file diff --git a/McdaToolkit/McdaMethods/Errors/ArraySizesAreNotEqual.cs b/McdaToolkit/McdaMethods/Errors/ArraySizesAreNotEqual.cs deleted file mode 100644 index d7ad7b2..0000000 --- a/McdaToolkit/McdaMethods/Errors/ArraySizesAreNotEqual.cs +++ /dev/null @@ -1,5 +0,0 @@ -using LightResults; - -namespace McdaToolkit.McdaMethods.Errors; - -public class ArraySizesAreNotEqual() : Error("Columns length of data matrix should be equal length of weights and types arrays"); \ No newline at end of file diff --git a/McdaToolkit/McdaMethods/Errors/CriteriaNotBetweenMinusOneAndOne.cs b/McdaToolkit/McdaMethods/Errors/CriteriaNotBetweenMinusOneAndOne.cs deleted file mode 100644 index b7dba8b..0000000 --- a/McdaToolkit/McdaMethods/Errors/CriteriaNotBetweenMinusOneAndOne.cs +++ /dev/null @@ -1,5 +0,0 @@ -using LightResults; - -namespace McdaToolkit.McdaMethods.Errors; - -public class CriteriaNotBetweenMinusOneAndOne() : Error("Criteria decision types should be number ∈Z{-1;1}"); \ No newline at end of file diff --git a/McdaToolkit/McdaMethods/Interfaces/IMethod.cs b/McdaToolkit/McdaMethods/Interfaces/IMethod.cs deleted file mode 100644 index 4639692..0000000 --- a/McdaToolkit/McdaMethods/Interfaces/IMethod.cs +++ /dev/null @@ -1,9 +0,0 @@ -using LightResults; -using MathNet.Numerics.LinearAlgebra; - -namespace McdaToolkit.McdaMethods.Interfaces; - -public interface IMethod -{ - Result> Calculate(double[,] matrix, double[] weights, int[] criteriaDirections); -} \ No newline at end of file diff --git a/McdaToolkit/McdaToolkit.csproj b/McdaToolkit/McdaToolkit.csproj deleted file mode 100644 index 9051825..0000000 --- a/McdaToolkit/McdaToolkit.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - enable - enable - - - - McdaToolkit - 2.1.0 - Jakub Tokarczyk - latest - README.md - netstandard2.1;net6.0;net7.0;net8.0 - © 2024 Jakub Tokarczyk - MIT - https://github.com/SarcasticMoose/mcda-toolkit - - - - - - - - - - - - - - - - diff --git a/McdaToolkit/Normalization/DataNormalizationService.cs b/McdaToolkit/Normalization/DataNormalizationService.cs deleted file mode 100644 index 168504e..0000000 --- a/McdaToolkit/Normalization/DataNormalizationService.cs +++ /dev/null @@ -1,25 +0,0 @@ - -using MathNet.Numerics.LinearAlgebra; -using McdaToolkit.Enums; -using McdaToolkit.Extensions; -using McdaToolkit.Normalization.Interfaces; -using McdaToolkit.NormalizationMethods.Interfaces; -namespace McdaToolkit.Normalization; - - -public class DataNormalizationService(NormalizationMethodEnum methodEnum) : IDataNormalization -{ - private readonly INormalize _method = NormalizationFactory.CreateNormalizationMethod(methodEnum); - - public Matrix NormalizeMatrix(Matrix matrix, int[] criteriaTypes) - { - foreach (var (col,index) in matrix.EnumerateColumns().Indexed()) - { - matrix.SetColumn(index, - criteriaTypes[index] == 1 - ? _method.Normalize(data: col, cost: false) - : _method.Normalize(data: col, cost: true)); - } - return matrix; - } -} diff --git a/McdaToolkit/Normalization/Interfaces/IDataNormalization.cs b/McdaToolkit/Normalization/Interfaces/IDataNormalization.cs deleted file mode 100644 index ef571b4..0000000 --- a/McdaToolkit/Normalization/Interfaces/IDataNormalization.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MathNet.Numerics.LinearAlgebra; - -namespace McdaToolkit.Normalization.Interfaces; - -public interface IDataNormalization -{ - Matrix NormalizeMatrix(Matrix matrix, int[] criteriaTypes); -} \ No newline at end of file diff --git a/McdaToolkit/NormalizationFactory.cs b/McdaToolkit/NormalizationFactory.cs deleted file mode 100644 index 2c4ce3c..0000000 --- a/McdaToolkit/NormalizationFactory.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Numerics; -using McdaToolkit.Enums; -using McdaToolkit.NormalizationMethods; -using McdaToolkit.NormalizationMethods.Interfaces; -using McdaToolkit.NormalizationMethods.Types.Linear; -using McdaToolkit.NormalizationMethods.Types.Sum; - -namespace McdaToolkit; - -internal static class NormalizationFactory -{ - public static INormalize CreateNormalizationMethod(NormalizationMethodEnum methodEnum) - { - return methodEnum switch - { - NormalizationMethodEnum.MinMax => new MinMaxNormalization(), - NormalizationMethodEnum.Vector => new VectorNormalization(), - _ => throw new Exception("Not existing normalization") - }; - } -} \ No newline at end of file diff --git a/McdaToolkit/NormalizationMethods/Interfaces/INormalize.cs b/McdaToolkit/NormalizationMethods/Interfaces/INormalize.cs deleted file mode 100644 index 8e7ab64..0000000 --- a/McdaToolkit/NormalizationMethods/Interfaces/INormalize.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MathNet.Numerics.LinearAlgebra; - -namespace McdaToolkit.NormalizationMethods.Interfaces; - -public interface INormalize where T : struct, IEquatable, IFormattable -{ - Vector Normalize(Vector data, bool cost); -} \ No newline at end of file diff --git a/McdaToolkit/NormalizationMethods/Types/Sum/VectorNormalization.cs b/McdaToolkit/NormalizationMethods/Types/Sum/VectorNormalization.cs deleted file mode 100644 index bf2329e..0000000 --- a/McdaToolkit/NormalizationMethods/Types/Sum/VectorNormalization.cs +++ /dev/null @@ -1,27 +0,0 @@ -using MathNet.Numerics.LinearAlgebra; -using McdaToolkit.NormalizationMethods.Interfaces; - -namespace McdaToolkit.NormalizationMethods.Types.Sum; - -public class VectorNormalization : INormalize -{ - /// - /// Create normalized vector using vector normalization method - /// - /// One-dimensional vector of data to normalize - /// Describe type of vector, cost or profit - /// - /// Return normalized vector - /// - public Vector Normalize(Vector data, bool cost) - { - var squaresOfSum = data / Math.Sqrt(data.PointwisePower(2).Sum()); - - if (cost) - { - return 1 - squaresOfSum; - } - - return squaresOfSum; - } -} \ No newline at end of file diff --git a/McdaToolkit/Options/McdaMethodOptions.cs b/McdaToolkit/Options/McdaMethodOptions.cs deleted file mode 100644 index ebbd381..0000000 --- a/McdaToolkit/Options/McdaMethodOptions.cs +++ /dev/null @@ -1,8 +0,0 @@ -using McdaToolkit.Enums; - -namespace McdaToolkit.Options; - -public class McdaMethodOptions -{ - public NormalizationMethodEnum NormalizationMethodEnum { get; set; } = NormalizationMethodEnum.MinMax; -} \ No newline at end of file diff --git a/src/McdaToolkit/Enums/NormalizationMethod.cs b/src/McdaToolkit/Enums/NormalizationMethod.cs new file mode 100644 index 0000000..a34afc7 --- /dev/null +++ b/src/McdaToolkit/Enums/NormalizationMethod.cs @@ -0,0 +1,10 @@ +namespace McdaToolkit.Enums; + +public enum NormalizationMethod +{ + MinMax, + Vector, + Logarithmic, + Sum, + Max +} \ No newline at end of file diff --git a/src/McdaToolkit/Extensions/EnumerableExtentions.cs b/src/McdaToolkit/Extensions/EnumerableExtentions.cs new file mode 100644 index 0000000..4dd86b9 --- /dev/null +++ b/src/McdaToolkit/Extensions/EnumerableExtentions.cs @@ -0,0 +1,40 @@ +namespace McdaToolkit.Extensions; + +internal static class EnumerableExtentions +{ + public static IEnumerable<(T item, int index)> Indexed(this IEnumerable source) + { + if (source is null) + { + throw new ArgumentNullException(); + } + + var i = 0; + foreach (var item in source) + { + yield return (item, i); + ++i; + } + } + + public static T[,] To2DArray(this IEnumerable> source) + { + var sourceToArray = source.ToArray(); + var rows = sourceToArray.Length; + var cols = sourceToArray[0].Count(); + var result = new T[rows, cols]; + var i = 0; + + foreach (var row in sourceToArray) + { + var j = 0; + foreach (var value in row) + { + result[i, j] = value; + j++; + } + i++; + } + return result; + } +} \ No newline at end of file diff --git a/McdaToolkit/McdaMethods/Helpers/CheckDataHelper.cs b/src/McdaToolkit/Mcda/Helpers/CheckDataHelper.cs similarity index 78% rename from McdaToolkit/McdaMethods/Helpers/CheckDataHelper.cs rename to src/McdaToolkit/Mcda/Helpers/CheckDataHelper.cs index 0149521..4f141d9 100644 --- a/McdaToolkit/McdaMethods/Helpers/CheckDataHelper.cs +++ b/src/McdaToolkit/Mcda/Helpers/CheckDataHelper.cs @@ -1,9 +1,6 @@ -using LightResults; -using MathNet.Numerics; -using MathNet.Numerics.LinearAlgebra; -using McdaToolkit.McdaMethods.Errors; +using MathNet.Numerics; -namespace McdaToolkit.McdaMethods.Helpers; +namespace McdaToolkit.Mcda.Helpers; public static class CheckDataHelper { diff --git a/src/McdaToolkit/Mcda/Helpers/Errors/DecisionCriteriaHaveIncorrectValueError.cs b/src/McdaToolkit/Mcda/Helpers/Errors/DecisionCriteriaHaveIncorrectValueError.cs new file mode 100644 index 0000000..dcf728e --- /dev/null +++ b/src/McdaToolkit/Mcda/Helpers/Errors/DecisionCriteriaHaveIncorrectValueError.cs @@ -0,0 +1,5 @@ +using LightResults; + +namespace McdaToolkit.Mcda.Helpers.Errors; + +public class DecisionCriteriaHaveIncorrectValueError() : Error("Criteria decision types should be number ∈Z{-1;1}"); \ No newline at end of file diff --git a/src/McdaToolkit/Mcda/Helpers/Errors/MatrixColumnLengthNotEqualWeightsVectorLengthError.cs b/src/McdaToolkit/Mcda/Helpers/Errors/MatrixColumnLengthNotEqualWeightsVectorLengthError.cs new file mode 100644 index 0000000..1c1b64a --- /dev/null +++ b/src/McdaToolkit/Mcda/Helpers/Errors/MatrixColumnLengthNotEqualWeightsVectorLengthError.cs @@ -0,0 +1,5 @@ +using LightResults; + +namespace McdaToolkit.Mcda.Helpers.Errors; + +public class MatrixColumnLengthNotEqualWeightsVectorLengthError() : Error("Columns length of data matrix should be equal length of weights and types arrays"); \ No newline at end of file diff --git a/McdaToolkit/McdaMethods/Errors/WeightNotSumToOneError.cs b/src/McdaToolkit/Mcda/Helpers/Errors/WeightNotSumToOneError.cs similarity index 70% rename from McdaToolkit/McdaMethods/Errors/WeightNotSumToOneError.cs rename to src/McdaToolkit/Mcda/Helpers/Errors/WeightNotSumToOneError.cs index 4c05572..5ddbdae 100644 --- a/McdaToolkit/McdaMethods/Errors/WeightNotSumToOneError.cs +++ b/src/McdaToolkit/Mcda/Helpers/Errors/WeightNotSumToOneError.cs @@ -1,5 +1,5 @@ using LightResults; -namespace McdaToolkit.McdaMethods.Errors; +namespace McdaToolkit.Mcda.Helpers.Errors; public class WeightNotSumToOneError() : Error("Sum of weight have to equal 1"); \ No newline at end of file diff --git a/src/McdaToolkit/Mcda/Methods/Abstraction/ICalculation.cs b/src/McdaToolkit/Mcda/Methods/Abstraction/ICalculation.cs new file mode 100644 index 0000000..1340658 --- /dev/null +++ b/src/McdaToolkit/Mcda/Methods/Abstraction/ICalculation.cs @@ -0,0 +1,17 @@ +using LightResults; +using MathNet.Numerics.LinearAlgebra; + +namespace McdaToolkit.Mcda.Methods.Abstraction; + +public interface ICalculation where TValue : struct, IEquatable, IFormattable +{ + /// + /// Calculate provided data + /// + /// Data as set of alternatives and theirs attributes + /// Data determining relevance of each attribute + /// Data that determines the columns is profit or cost + /// Vector of processed data in descending order + Result> Calculate(IEnumerable> matrix, IEnumerable weights, IEnumerable criteriaDirections); +} + diff --git a/src/McdaToolkit/Mcda/Methods/Abstraction/IMcdaMethod.cs b/src/McdaToolkit/Mcda/Methods/Abstraction/IMcdaMethod.cs new file mode 100644 index 0000000..799fc81 --- /dev/null +++ b/src/McdaToolkit/Mcda/Methods/Abstraction/IMcdaMethod.cs @@ -0,0 +1,6 @@ +namespace McdaToolkit.Mcda.Methods.Abstraction; + +public interface IMcdaMethod : ICalculation +{ + +} \ No newline at end of file diff --git a/src/McdaToolkit/Mcda/Methods/Abstraction/McdaMethod.cs b/src/McdaToolkit/Mcda/Methods/Abstraction/McdaMethod.cs new file mode 100644 index 0000000..adfd888 --- /dev/null +++ b/src/McdaToolkit/Mcda/Methods/Abstraction/McdaMethod.cs @@ -0,0 +1,45 @@ +using LightResults; +using MathNet.Numerics.LinearAlgebra; +using McdaToolkit.Extensions; +using McdaToolkit.Mcda.Services; + +namespace McdaToolkit.Mcda.Methods.Abstraction; + +public abstract class McdaMethod : IMcdaMethod +{ + private readonly MatrixCheckerService _matrixCheckerService = new(); + + protected abstract Result> RunCalculation(double[,] matrix, double[] weights, int[] criteriaDirections); + + /// + public Result> Calculate(IEnumerable> matrix, IEnumerable weights, IEnumerable criteriaDirections) + { + var convertedMatrix = matrix.To2DArray(); + var convertedWeights = weights.ToArray(); + var convertedCriteriaDecision = criteriaDirections.ToArray(); + var matrixChecked = _matrixCheckerService.ValidateData(convertedMatrix, convertedWeights, convertedCriteriaDecision); + + if (matrixChecked.IsFailed) + { + return Result.Fail>(); + } + return RunCalculation(convertedMatrix, convertedWeights, convertedCriteriaDecision); + } + + /// + /// Calculate provided data + /// + /// Data as set of alternatives and theirs attributes + /// Data determining relevance of each attribute + /// Data that determines the columns is profit or cost + /// Vector of processed data in descending order + public Result> Calculate(double[,] matrix,double[] weights,int[] criteriaDirections) + { + var matrixChecked = _matrixCheckerService.ValidateData(matrix, weights, criteriaDirections); + if (matrixChecked.IsFailed) + { + return Result.Fail>(); + } + return RunCalculation(matrix, weights, criteriaDirections); + } +} \ No newline at end of file diff --git a/McdaToolkit/McdaMethods/TopsisMethod.cs b/src/McdaToolkit/Mcda/Methods/Topsis.cs similarity index 65% rename from McdaToolkit/McdaMethods/TopsisMethod.cs rename to src/McdaToolkit/Mcda/Methods/Topsis.cs index 1ede244..020e68d 100644 --- a/McdaToolkit/McdaMethods/TopsisMethod.cs +++ b/src/McdaToolkit/Mcda/Methods/Topsis.cs @@ -1,44 +1,22 @@ using LightResults; using MathNet.Numerics.LinearAlgebra; -using McdaToolkit.Enums; -using McdaToolkit.McdaMethods.Abstraction; -using McdaToolkit.McdaMethods.Interfaces; -using McdaToolkit.Normalization; -using McdaToolkit.Options; +using McdaToolkit.Mcda.Methods.Abstraction; +using McdaToolkit.Mcda.Options; +using McdaToolkit.Normalization.Service; +using McdaToolkit.Normalization.Service.Abstraction; -namespace McdaToolkit.McdaMethods; +namespace McdaToolkit.Mcda.Methods; -public class TopsisMethod : McdaMethod +public sealed class Topsis : McdaMethod { - private readonly DataNormalizationService _normalizationServiceService; - - public TopsisMethod() - { - _normalizationServiceService = new DataNormalizationService(NormalizationMethodEnum.MinMax); - } - - public TopsisMethod(McdaMethodOptions options) - { - _normalizationServiceService = new DataNormalizationService(options.NormalizationMethodEnum); - } + private readonly IMatrixNormalizationService _normalizationServiceServiceService; - protected override Result> Calculate(Matrix matrix, double[] weights, - int[] criteriaDirections) + public Topsis(McdaMethodOptions options) { - var normalizedMatrix = _normalizationServiceService.NormalizeMatrix(matrix, criteriaDirections); - var weightedMatrix = WeightedMatrix(normalizedMatrix, weights); - - var idealBest = IdealValues(weightedMatrix, true); - var idealWorst = IdealValues(weightedMatrix, false); - - var distanceToBest = CalculateEuclideanDistance(weightedMatrix, idealBest); - var distanceToWorst = CalculateEuclideanDistance(weightedMatrix, idealWorst); - var topsisScores = CalculateTopsisScores(distanceToBest, distanceToWorst); - - return Result.Ok(topsisScores); + _normalizationServiceServiceService = new MatrixNormalizatorService(options.NormalizationMethod); } - - private Matrix WeightedMatrix(Matrix matrix, double[] weights) + + private Matrix WeightedMatrix(Matrix matrix, Vector weights) { for (int i = 0; i < matrix.RowCount; i++) { @@ -47,7 +25,6 @@ private Matrix WeightedMatrix(Matrix matrix, double[] weights) matrix[i, j] *= weights[j]; } } - return matrix; } @@ -61,8 +38,7 @@ private Vector IdealValues(Matrix matrix, bool pis) }); } - private Vector CalculateEuclideanDistance(Matrix matrix, - Vector ideal) + private Vector CalculateEuclideanDistance(Matrix matrix, Vector ideal) { return Vector.Build .DenseOfArray(matrix @@ -79,4 +55,21 @@ private Vector CalculateTopsisScores(Vector distanceToBest, Vect { return distanceToWorst.PointwiseDivide(distanceToBest.Add(distanceToWorst)); } + + protected override Result> RunCalculation(double[,] matrix,double[] weights,int[] criteriaDirections) + { + var matrixBuilded = Matrix.Build.DenseOfArray(matrix); + var weightsBuilded = Vector.Build.DenseOfArray(weights); + var normalizedMatrix = _normalizationServiceServiceService.NormalizeMatrix(matrixBuilded, criteriaDirections); + var weightedMatrix = WeightedMatrix(normalizedMatrix, weightsBuilded); + + var idealBest = IdealValues(weightedMatrix, true); + var idealWorst = IdealValues(weightedMatrix, false); + + var distanceToBest = CalculateEuclideanDistance(weightedMatrix, idealBest); + var distanceToWorst = CalculateEuclideanDistance(weightedMatrix, idealWorst); + var topsisScores = CalculateTopsisScores(distanceToBest, distanceToWorst); + + return Result.Ok(topsisScores); + } } \ No newline at end of file diff --git a/src/McdaToolkit/Mcda/Options/McdaMethodOptions.cs b/src/McdaToolkit/Mcda/Options/McdaMethodOptions.cs new file mode 100644 index 0000000..a9cd065 --- /dev/null +++ b/src/McdaToolkit/Mcda/Options/McdaMethodOptions.cs @@ -0,0 +1,14 @@ +using McdaToolkit.Enums; + +namespace McdaToolkit.Mcda.Options; + +/// +/// Configuration for Mcda methods +/// +public record McdaMethodOptions +{ + /// + /// Current normalization method + /// + public NormalizationMethod NormalizationMethod { get; set; } = NormalizationMethod.MinMax; +} \ No newline at end of file diff --git a/src/McdaToolkit/Mcda/Services/MatrixCheckerService.cs b/src/McdaToolkit/Mcda/Services/MatrixCheckerService.cs new file mode 100644 index 0000000..83c72ca --- /dev/null +++ b/src/McdaToolkit/Mcda/Services/MatrixCheckerService.cs @@ -0,0 +1,30 @@ +using LightResults; +using McdaToolkit.Mcda.Helpers; +using McdaToolkit.Mcda.Helpers.Errors; + +namespace McdaToolkit.Mcda.Services; + +internal sealed class MatrixCheckerService +{ + public Result ValidateData(double[,] matrix, double[] weights, int[] criteriaDirections) + { + var isWeightsCorrect = CheckDataHelper.IsWeightEqualOne(weights); + if (!isWeightsCorrect) + { + return Result.Fail(new WeightNotSumToOneError()); + } + + var isCriteriaDecisionCorrect = CheckDataHelper.IsCriteriaDesisionBetweenMinusOneAndOne(criteriaDirections); + if (!isCriteriaDecisionCorrect) + { + return Result.Fail(new DecisionCriteriaHaveIncorrectValueError()); + } + + var isSizesAreCorrect = CheckDataHelper.IsDataWeightsAndTypesHaveCorrectSizes(matrix, weights, criteriaDirections); + if (!isSizesAreCorrect) + { + return Result.Fail(new MatrixColumnLengthNotEqualWeightsVectorLengthError()); + } + return Result.Ok(); + } +} \ No newline at end of file diff --git a/src/McdaToolkit/McdaToolkit.csproj b/src/McdaToolkit/McdaToolkit.csproj new file mode 100644 index 0000000..612999c --- /dev/null +++ b/src/McdaToolkit/McdaToolkit.csproj @@ -0,0 +1,34 @@ + + + + enable + enable + + + + McdaToolkit + 2.1.0 + Jakub Tokarczyk + latest + README.md + netstandard2.1;net6.0;net7.0;net8.0 + © 2024 Jakub Tokarczyk + MIT + https://github.com/SarcasticMoose/mcda-toolkit + icon.png + + + + + + + + + + + + + + + + diff --git a/src/McdaToolkit/Normalization/Methods/Abstraction/INormalizationMethod.cs b/src/McdaToolkit/Normalization/Methods/Abstraction/INormalizationMethod.cs new file mode 100644 index 0000000..87ccce5 --- /dev/null +++ b/src/McdaToolkit/Normalization/Methods/Abstraction/INormalizationMethod.cs @@ -0,0 +1,9 @@ +namespace McdaToolkit.Normalization.Methods.Abstraction; + +/// +/// Marker interface indicates normalization method abstraction +/// +internal interface INormalizationMethod : IVectorNormalizator +{ + +} \ No newline at end of file diff --git a/src/McdaToolkit/Normalization/Methods/Abstraction/IVectorNormalizator.cs b/src/McdaToolkit/Normalization/Methods/Abstraction/IVectorNormalizator.cs new file mode 100644 index 0000000..741e3b5 --- /dev/null +++ b/src/McdaToolkit/Normalization/Methods/Abstraction/IVectorNormalizator.cs @@ -0,0 +1,19 @@ +using MathNet.Numerics.LinearAlgebra; + +namespace McdaToolkit.Normalization.Methods.Abstraction; + +/// +/// Vector normalization generic abstraction +/// +internal interface IVectorNormalizator where T : struct, IEquatable, IFormattable +{ + /// + /// Normalize provided vector + /// + /// One-dimensional vector of data to normalize + /// Describe type of vector, cost or profit + /// + /// Return normalized vector + /// + Vector Normalize(Vector data, bool cost); +} \ No newline at end of file diff --git a/src/McdaToolkit/Normalization/Methods/Linear/MaxNormalization.cs b/src/McdaToolkit/Normalization/Methods/Linear/MaxNormalization.cs new file mode 100644 index 0000000..26848b8 --- /dev/null +++ b/src/McdaToolkit/Normalization/Methods/Linear/MaxNormalization.cs @@ -0,0 +1,17 @@ +using MathNet.Numerics.LinearAlgebra; +using McdaToolkit.Normalization.Methods.Abstraction; + +namespace McdaToolkit.Normalization.Methods.Linear; + +internal class MaxNormalization : INormalizationMethod +{ + /// + public Vector Normalize(Vector data, bool cost) + { + if (cost) + { + return 1 - data / data.Maximum(); + } + return data / data.Maximum(); + } +} \ No newline at end of file diff --git a/McdaToolkit/NormalizationMethods/Types/Linear/MinMaxNormalization.cs b/src/McdaToolkit/Normalization/Methods/Linear/MinMaxNormalization.cs similarity index 59% rename from McdaToolkit/NormalizationMethods/Types/Linear/MinMaxNormalization.cs rename to src/McdaToolkit/Normalization/Methods/Linear/MinMaxNormalization.cs index ea41c07..f04fc5a 100644 --- a/McdaToolkit/NormalizationMethods/Types/Linear/MinMaxNormalization.cs +++ b/src/McdaToolkit/Normalization/Methods/Linear/MinMaxNormalization.cs @@ -1,19 +1,20 @@ using MathNet.Numerics.LinearAlgebra; -using McdaToolkit.NormalizationMethods.Interfaces; +using McdaToolkit.Normalization.Methods.Abstraction; -namespace McdaToolkit.NormalizationMethods.Types.Linear; +namespace McdaToolkit.Normalization.Methods.Linear; -public class MinMaxNormalization : INormalize +internal class MinMaxNormalization : INormalizationMethod { - private const double MaxError = 1.11e-16; + private const double MaxTolerance = 1.11e-16; + /// public Vector Normalize(Vector data, bool cost = false) { var max = data.Maximum(); var min = data.Minimum(); var difference = max - min; - if (Math.Abs(difference) < MaxError) + if (Math.Abs(difference) < MaxTolerance) { return Vector.Build.Dense(data.Count, _ => 1); } diff --git a/src/McdaToolkit/Normalization/Methods/Linear/SumNormalization.cs b/src/McdaToolkit/Normalization/Methods/Linear/SumNormalization.cs new file mode 100644 index 0000000..8caf5ae --- /dev/null +++ b/src/McdaToolkit/Normalization/Methods/Linear/SumNormalization.cs @@ -0,0 +1,18 @@ +using MathNet.Numerics.LinearAlgebra; +using McdaToolkit.Normalization.Methods.Abstraction; + +namespace McdaToolkit.Normalization.Methods.Linear; + +internal class SumNormalization : INormalizationMethod +{ + /// + public Vector Normalize(Vector data, bool cost) + { + if (cost) + { + return 1 / data / data.Sum(x => 1/x); + } + + return data / data.Sum(); + } +} \ No newline at end of file diff --git a/src/McdaToolkit/Normalization/Methods/Sum/LogarithmicNormalization.cs b/src/McdaToolkit/Normalization/Methods/Sum/LogarithmicNormalization.cs new file mode 100644 index 0000000..08732bd --- /dev/null +++ b/src/McdaToolkit/Normalization/Methods/Sum/LogarithmicNormalization.cs @@ -0,0 +1,21 @@ +using MathNet.Numerics.LinearAlgebra; +using McdaToolkit.Normalization.Methods.Abstraction; + +namespace McdaToolkit.Normalization.Methods.Sum; + +internal class LogarithmicNormalization : INormalizationMethod +{ + /// + public Vector Normalize(Vector data, bool cost) + { + var product = data.Aggregate(1.0,(x,y) => x * y); + var exp = data.PointwiseLog() / Math.Log(product); + + if (cost) + { + return 1 - exp; + } + + return exp; + } +} \ No newline at end of file diff --git a/src/McdaToolkit/Normalization/Methods/Sum/VectorNormalization.cs b/src/McdaToolkit/Normalization/Methods/Sum/VectorNormalization.cs new file mode 100644 index 0000000..1025015 --- /dev/null +++ b/src/McdaToolkit/Normalization/Methods/Sum/VectorNormalization.cs @@ -0,0 +1,18 @@ +using MathNet.Numerics.LinearAlgebra; +using McdaToolkit.Normalization.Methods.Abstraction; + +namespace McdaToolkit.Normalization.Methods.Sum; + +internal class VectorNormalization : INormalizationMethod +{ + /// + public Vector Normalize(Vector data, bool cost) + { + var squaresOfSum = data / Math.Sqrt(data.PointwisePower(2).Sum()); + if (cost) + { + return 1 - squaresOfSum; + } + return squaresOfSum; + } +} \ No newline at end of file diff --git a/src/McdaToolkit/Normalization/Service/Abstraction/IMatrixNormalizationService.cs b/src/McdaToolkit/Normalization/Service/Abstraction/IMatrixNormalizationService.cs new file mode 100644 index 0000000..3444068 --- /dev/null +++ b/src/McdaToolkit/Normalization/Service/Abstraction/IMatrixNormalizationService.cs @@ -0,0 +1,10 @@ +using LightResults; +using McdaToolkit.Enums; + +namespace McdaToolkit.Normalization.Service.Abstraction; + +internal interface IMatrixNormalizationService : IMatrixNormalizator +{ + public NormalizationMethod GetCurrentNormalizationName { get; } + public Result ChangeNormalizationMethod(NormalizationMethod newMethod); +} \ No newline at end of file diff --git a/src/McdaToolkit/Normalization/Service/Abstraction/IMatrixNormalizator.cs b/src/McdaToolkit/Normalization/Service/Abstraction/IMatrixNormalizator.cs new file mode 100644 index 0000000..d2b7157 --- /dev/null +++ b/src/McdaToolkit/Normalization/Service/Abstraction/IMatrixNormalizator.cs @@ -0,0 +1,17 @@ +using MathNet.Numerics.LinearAlgebra; + +namespace McdaToolkit.Normalization.Service.Abstraction; + +/// +/// Matrix normalization generic abstraction +/// +internal interface IMatrixNormalizator where T : struct, IEquatable, IFormattable +{ + /// + /// Normalize provided matrix + /// + /// One-dimensional vector of data to normalize + /// Describe type of vector, cost or profit + /// Return normalized matrix + Matrix NormalizeMatrix(Matrix matrix, int[] criteriaTypes); +} \ No newline at end of file diff --git a/src/McdaToolkit/Normalization/Service/MatrixNormalizatorService.cs b/src/McdaToolkit/Normalization/Service/MatrixNormalizatorService.cs new file mode 100644 index 0000000..abcb8f0 --- /dev/null +++ b/src/McdaToolkit/Normalization/Service/MatrixNormalizatorService.cs @@ -0,0 +1,42 @@ +using LightResults; +using MathNet.Numerics.LinearAlgebra; +using McdaToolkit.Enums; +using McdaToolkit.Extensions; +using McdaToolkit.Normalization.Methods.Abstraction; +using McdaToolkit.Normalization.Service.Abstraction; + +namespace McdaToolkit.Normalization.Service; + +public sealed class MatrixNormalizatorService : IMatrixNormalizationService +{ + private IVectorNormalizator _vectorNormalizatorMethod; + + public MatrixNormalizatorService(NormalizationMethod name) + { + GetCurrentNormalizationName = name; + _vectorNormalizatorMethod = NormalizationMethodFactory.Create(GetCurrentNormalizationName); + } + + /// + public Matrix NormalizeMatrix(Matrix matrix, int[] criteriaTypes) + { + foreach (var (col, index) in matrix.EnumerateColumns().Indexed()) + matrix.SetColumn(columnIndex: index, + column: criteriaTypes[index] == 1 + ? _vectorNormalizatorMethod.Normalize(data: col, cost: false) + : _vectorNormalizatorMethod.Normalize(data: col, cost: true)); + return matrix; + } + + public NormalizationMethod GetCurrentNormalizationName { get; } + + public Result ChangeNormalizationMethod(NormalizationMethod newMethod) + { + if (newMethod == GetCurrentNormalizationName) + { + return Result.Fail(NormalizationServiceErrors.MethodsEqual()); + } + _vectorNormalizatorMethod = NormalizationMethodFactory.Create(newMethod); + return Result.Ok(); + } +} \ No newline at end of file diff --git a/src/McdaToolkit/Normalization/Service/NormalizationServiceErrors.cs b/src/McdaToolkit/Normalization/Service/NormalizationServiceErrors.cs new file mode 100644 index 0000000..53679af --- /dev/null +++ b/src/McdaToolkit/Normalization/Service/NormalizationServiceErrors.cs @@ -0,0 +1,8 @@ +using LightResults; + +namespace McdaToolkit.Normalization.Service; + +public static class NormalizationServiceErrors +{ + public static Error MethodsEqual() => new("New method have to be diffrent than current one"); +} \ No newline at end of file diff --git a/src/McdaToolkit/NormalizationMethodFactory.cs b/src/McdaToolkit/NormalizationMethodFactory.cs new file mode 100644 index 0000000..f3d346a --- /dev/null +++ b/src/McdaToolkit/NormalizationMethodFactory.cs @@ -0,0 +1,22 @@ +using McdaToolkit.Enums; +using McdaToolkit.Normalization.Methods.Abstraction; +using McdaToolkit.Normalization.Methods.Linear; +using McdaToolkit.Normalization.Methods.Sum; + +namespace McdaToolkit; + +internal static class NormalizationMethodFactory +{ + public static IVectorNormalizator Create(NormalizationMethod method) + { + return method switch + { + NormalizationMethod.MinMax => new MinMaxNormalization(), + NormalizationMethod.Vector => new VectorNormalization(), + NormalizationMethod.Logarithmic => new LogarithmicNormalization(), + NormalizationMethod.Sum => new SumNormalization(), + NormalizationMethod.Max => new MaxNormalization(), + _ => throw new Exception("Not existing normalization") + }; + } +} \ No newline at end of file diff --git a/tests/McdaToolkit.UnitTests/MatrixCheckerTests.cs b/tests/McdaToolkit.UnitTests/MatrixCheckerTests.cs new file mode 100644 index 0000000..7927503 --- /dev/null +++ b/tests/McdaToolkit.UnitTests/MatrixCheckerTests.cs @@ -0,0 +1,73 @@ +using FluentAssertions; +using McdaToolkit.Mcda.Helpers.Errors; +using McdaToolkit.Mcda.Services; + +namespace McdaToolkit.UnitTests; + +public class MatrixCheckerTests +{ + [Fact] + public void ValidateData_WeightAreNotEqualOne_ShouldReturnResultFail() + { + double[,] matrix = { + { 66, 56, 95 }, + { 61, 55, 166 }, + { 65, 49, 113 }, + { 95, 56, 99 }, + { 63, 43, 178 }, + { 74, 59, 140 }, + }; + double[] weights = [0.8,0.25,0.35]; + int[] types = [-1, -1, 1]; + var matrixCheckerService = new MatrixCheckerService(); + + var result = matrixCheckerService.ValidateData(matrix, weights, types); + + result.IsSuccess.Should().BeFalse(); + result.HasError(); + } + + [Fact] + public void Calculate_DecisionCriteriaAreNotBetweenMinusOneAndOne_ShouldReturnResultFail() + { + var matrix = new double[,] + { + { 66, 56, 95 }, + { 61, 55, 166 }, + { 65, 49, 113 }, + { 95, 56, 99 }, + { 63, 43, 178 }, + { 74, 59, 140 }, + }; + double[] weights = [0.4,0.25,0.35]; + int[] types = [-1, -2, 1]; + var matrixCheckerService = new MatrixCheckerService(); + + var result = matrixCheckerService.ValidateData(matrix, weights, types); + + result.IsSuccess.Should().BeFalse(); + result.HasError(); + } + + [Fact] + public void Calculate_DimensionsOfAllMatrixesNotTheSame_ShouldReturnResultFail() + { + var matrix = new double[,] + { + { 66, 56, 95 }, + { 61, 55, 166 }, + { 65, 49, 113 }, + { 95, 56, 99 }, + { 63, 43, 178 }, + { 74, 59, 140 }, + }; + double[] weights = [0.4,0.25,0.15,0.20]; + int[] types = [-1, -1, 1, 1]; + var matrixCheckerService = new MatrixCheckerService(); + + var result = matrixCheckerService.ValidateData(matrix, weights, types); + + result.IsSuccess.Should().BeFalse(); + result.HasError(); + } +} \ No newline at end of file diff --git a/tests/McdaToolkit.UnitTests/McdaMethodsTests.cs b/tests/McdaToolkit.UnitTests/McdaMethodsTests.cs new file mode 100644 index 0000000..f92aa6b --- /dev/null +++ b/tests/McdaToolkit.UnitTests/McdaMethodsTests.cs @@ -0,0 +1,40 @@ +using FluentAssertions; +using MathNet.Numerics; +using McdaToolkit.Enums; +using McdaToolkit.Mcda.Methods; +using McdaToolkit.Mcda.Options; + +namespace McdaToolkit.UnitTests; + +public class McdaMethodsTests +{ + [Fact] + public void Calculate_TopsisMethod_ShouldBeEqualToExpected() + { + var matrix = new double[,] + { + { 66, 56, 95 }, + { 61, 55, 166 }, + { 65, 49, 113 }, + { 95, 56, 99 }, + { 63, 43, 178 }, + { 74, 59, 140 }, + }; + double[] weights = [0.4,0.25,0.35]; + int[] types = [-1, -1, 1]; + double[] expectedTopsisScore = [0.38805147,0.76189759,0.58509479,0.06374247,0.97647059,0.43681786]; + + var topsis = new Topsis(new McdaMethodOptions() + { + NormalizationMethod = NormalizationMethod.MinMax + }); + + var topsisResult = topsis.Calculate(matrix,weights,types); + var final = topsisResult.Value; + + final.Enumerate() + .Select(x => x.Round(8)) + .Should() + .BeEquivalentTo(expectedTopsisScore); + } +} \ No newline at end of file diff --git a/McdaToolkit.UnitTests/McdaToolkit.UnitTests.csproj b/tests/McdaToolkit.UnitTests/McdaToolkit.UnitTests.csproj similarity index 91% rename from McdaToolkit.UnitTests/McdaToolkit.UnitTests.csproj rename to tests/McdaToolkit.UnitTests/McdaToolkit.UnitTests.csproj index b428fc1..961d6a9 100644 --- a/McdaToolkit.UnitTests/McdaToolkit.UnitTests.csproj +++ b/tests/McdaToolkit.UnitTests/McdaToolkit.UnitTests.csproj @@ -22,7 +22,7 @@ - + diff --git a/tests/McdaToolkit.UnitTests/NormalizationTests.cs b/tests/McdaToolkit.UnitTests/NormalizationTests.cs new file mode 100644 index 0000000..684143a --- /dev/null +++ b/tests/McdaToolkit.UnitTests/NormalizationTests.cs @@ -0,0 +1,131 @@ +using FluentAssertions; +using MathNet.Numerics.LinearAlgebra; +using McdaToolkit.Enums; +using McdaToolkit.Normalization.Service; + +namespace McdaToolkit.UnitTests; + +public class NormalizationTests +{ + private readonly Matrix _matrixToNormalize = Matrix.Build.DenseOfArray(new[,] + { + { 32.57, 14.56, 87.12, 56.34, 47.89 }, + { 93.23, 76.34, 33.78, 25.68, 64.23 }, + { 78.45, 68.92, 45.67, 87.34, 39.45 }, + { 54.12, 34.56, 56.89, 92.56, 81.56 }, + { 21.76, 53.23, 73.21, 65.78, 57.34 }, + { 85.34, 98.45, 38.45, 23.45, 62.89 }, + { 42.68, 27.34, 49.67, 74.12, 53.21 } + }); + + private readonly int[] _types = [-1, -1, 1, 1, -1]; + + [Fact] + public void Normalize_MinMaxNormalization_ShouldReturnedExpectedValues() + { + var expected = new double[][] + { + [0.84874773, 1, 1, 0.47590797, 0.79957255], + [0, 0.26355942, 0, 0.03226740, 0.41154120], + [0.20680006, 0.35200858, 0.22290964, 0.92446824, 1], + [0.54722261, 0.76159256, 0.43325834, 1, 0], + [1, 0.53903922, 0.73922010, 0.61250181, 0.57516029], + [0.11039597, 0, 0.08755156, 0, 0.44336262], + [0.70728977, 0.84765765, 0.29790026, 0.73317899, 0.67323676] + }; + var dataNormalization = new MatrixNormalizatorService(NormalizationMethod.MinMax); + + var normalizedMatrix = dataNormalization.NormalizeMatrix(_matrixToNormalize,_types); + + var equalityResult = TestHelpers.CheckEquality(normalizedMatrix, expected); + equalityResult.Should().BeTrue(); + } + + [Fact] + public void Normalize_VectorNormalization_ShouldReturnedExpectedValues() + { + var normalizationType = NormalizationMethod.Vector; + var expected = new double[][] + { + [0.80678026, 0.90838501, 0.57002794, 0.32313221, 0.69529318], + [0.44691814, 0.51965053, 0.22102323, 0.14728497, 0.59132764], + [0.53459968, 0.56633894, 0.29881974, 0.50092948, 0.74899386], + [0.67893607, 0.78254024, 0.37223243, 0.53086825, 0.48106309], + [0.87090999, 0.66506416, 0.47901452, 0.37727434, 0.63516623], + [0.49372513, 0.38052914, 0.25157913, 0.13449503, 0.59985358], + [0.74680324, 0.82797021, 0.32499182, 0.42510755, 0.66144393] + }; + var dataNormalization = new MatrixNormalizatorService(normalizationType); + + var normalizedMatrix = dataNormalization.NormalizeMatrix(_matrixToNormalize,_types); + + var equalityResult = TestHelpers.CheckEquality(normalizedMatrix, expected); + equalityResult.Should().BeTrue(); + } + + [Fact] + public void Normalize_Logarithmic_ShouldReturnedExpectedValues() + { + var normalizationType = NormalizationMethod.Logarithmic; + var expected = new double[][] + { + [0.87403010, 0.89954564, 0.16128664, 0.14438272, 0.86315596], + [0.83599827, 0.83739945, 0.12708112, 0.11624356, 0.85277256], + [0.84224030, 0.84123458, 0.13796910, 0.16008394, 0.87001329], + [0.85566609, 0.86712382, 0.14590034, 0.16216292, 0.84432373], + [0.88861531, 0.85092357, 0.15500620, 0.14993079, 0.85678609], + [0.83919604, 0.82785947, 0.13175623, 0.11299010, 0.85351828], + [0.86425385, 0.87591345, 0.14100037, 0.15420595, 0.85943008] + }; + var dataNormalization = new MatrixNormalizatorService(normalizationType); + + var normalizedMatrix = dataNormalization.NormalizeMatrix(_matrixToNormalize,_types); + + var equalityResult = TestHelpers.CheckEquality(normalizedMatrix, expected); + equalityResult.Should().BeTrue(); + } + + [Fact] + public void Normalize_Sum_ShouldReturnedExpectedValues() + { + var normalizationType = NormalizationMethod.Sum; + var expected = new double[][] + { + [0.19968511, 0.36006754, 0.22640921, 0.13248054, 0.16546924], + [0.06976021, 0.06867413, 0.08778814, 0.06038517, 0.12337416], + [0.08290305, 0.07606766, 0.11868812, 0.20537541, 0.20087001], + [0.12017266, 0.15169512, 0.14784688, 0.21764996, 0.09715942], + [0.29888530, 0.09848926, 0.19025962, 0.15467820, 0.13819885], + [0.07620980, 0.05325123, 0.09992463, 0.05514144, 0.12600289], + [0.15238388, 0.19175506, 0.12908340, 0.17428928, 0.14892543] + }; + var dataNormalization = new MatrixNormalizatorService(normalizationType); + + var normalizedMatrix = dataNormalization.NormalizeMatrix(_matrixToNormalize,_types); + + var equalityResult = TestHelpers.CheckEquality(normalizedMatrix, expected); + equalityResult.Should().BeTrue(); + } + + [Fact] + public void Normalize_Max_ShouldReturnedExpectedValues() + { + var normalizationType = NormalizationMethod.Max; + var expected = new double[][] + { + [0.65064893, 0.85210767, 1, 0.60868626, 0.41282491], + [0, 0.22458101, 0.38774105, 0.27744166, 0.21248161], + [0.15853266, 0.29994921, 0.52421947, 0.94360415, 0.51630701], + [0.41950016, 0.64895886, 0.65300735, 1, 0], + [0.76659873, 0.45931945, 0.84033517, 0.71067416, 0.29695929], + [0.08462941, 0, 0.44134527, 0.25334918, 0.22891123], + [0.54220744, 0.72229558, 0.57013315, 0.80077787, 0.34759686] + }; + var dataNormalization = new MatrixNormalizatorService(normalizationType); + + var normalizedMatrix = dataNormalization.NormalizeMatrix(_matrixToNormalize,_types); + + var equalityResult = TestHelpers.CheckEquality(normalizedMatrix, expected); + equalityResult.Should().BeTrue(); + } +} \ No newline at end of file diff --git a/McdaToolkit.UnitTests/Helpers/TestHelpers.cs b/tests/McdaToolkit.UnitTests/TestHelpers.cs similarity index 95% rename from McdaToolkit.UnitTests/Helpers/TestHelpers.cs rename to tests/McdaToolkit.UnitTests/TestHelpers.cs index 298a715..3b4e962 100644 --- a/McdaToolkit.UnitTests/Helpers/TestHelpers.cs +++ b/tests/McdaToolkit.UnitTests/TestHelpers.cs @@ -1,7 +1,7 @@ using MathNet.Numerics; using MathNet.Numerics.LinearAlgebra; -namespace McdaToolkit.UnitTests.Helpers; +namespace McdaToolkit.UnitTests; public abstract class TestHelpers {