diff --git a/Sources/Accord.Math/Accord.Math.csproj b/Sources/Accord.Math/Accord.Math.csproj index 610b3ef8f..dd0ba6b64 100644 --- a/Sources/Accord.Math/Accord.Math.csproj +++ b/Sources/Accord.Math/Accord.Math.csproj @@ -217,6 +217,7 @@ <Compile Include="Matrix\Vector.MinMax.cs" /> <Compile Include="Matrix\Matrix.MinMax.cs" /> <Compile Include="Optimization\Base\IFunctionOptimizationMethod.cs" /> + <Compile Include="Optimization\Constrained\Constraints\ConstraintExtensions.cs" /> <Compile Include="Optimization\Losses\BinaryCrossEntropyLoss.cs" /> <Compile Include="Optimization\Losses\CategoryCrossEntropyLoss.cs" /> <Compile Include="Optimization\Losses\HammingLoss.cs" /> diff --git a/Sources/Accord.Math/Optimization/Constrained/AugmentedLagrangian.cs b/Sources/Accord.Math/Optimization/Constrained/AugmentedLagrangian.cs index d38a381ac..fffe98c59 100644 --- a/Sources/Accord.Math/Optimization/Constrained/AugmentedLagrangian.cs +++ b/Sources/Accord.Math/Optimization/Constrained/AugmentedLagrangian.cs @@ -56,7 +56,7 @@ public enum AugmentedLagrangianStatus /// <summary> /// The optimization could not make progress towards finding a feasible - /// solution. Try increasing the <see cref="NonlinearConstraint.Tolerance"/> + /// solution. Try increasing the <see cref="IConstraint.Tolerance"/> /// of the constraints. /// </summary> /// @@ -115,9 +115,9 @@ public class AugmentedLagrangian : BaseGradientOptimizationMethod, IGradientOpti IGradientOptimizationMethod dualSolver; - NonlinearConstraint[] lesserThanConstraints; - NonlinearConstraint[] greaterThanConstraints; - NonlinearConstraint[] equalityConstraints; + IConstraint[] lesserThanConstraints; + IConstraint[] greaterThanConstraints; + IConstraint[] equalityConstraints; private double rho; @@ -206,9 +206,9 @@ public int MaxEvaluations /// /// <param name="numberOfVariables">The number of free parameters in the optimization problem.</param> /// <param name="constraints"> - /// The <see cref="NonlinearConstraint"/>s to which the solution must be subjected.</param> + /// The <see cref="IConstraint"/>s to which the solution must be subjected.</param> /// - public AugmentedLagrangian(int numberOfVariables, IEnumerable<NonlinearConstraint> constraints) + public AugmentedLagrangian(int numberOfVariables, IEnumerable<IConstraint> constraints) : base(numberOfVariables) { init(null, constraints, null); @@ -220,9 +220,9 @@ public AugmentedLagrangian(int numberOfVariables, IEnumerable<NonlinearConstrain /// /// <param name="function">The objective function to be optimized.</param> /// <param name="constraints"> - /// The <see cref="NonlinearConstraint"/>s to which the solution must be subjected.</param> + /// The <see cref="IConstraint"/>s to which the solution must be subjected.</param> /// - public AugmentedLagrangian(NonlinearObjectiveFunction function, IEnumerable<NonlinearConstraint> constraints) + public AugmentedLagrangian(NonlinearObjectiveFunction function, IEnumerable<IConstraint> constraints) : base(function.NumberOfVariables) { init(function, constraints, null); @@ -237,10 +237,10 @@ public AugmentedLagrangian(NonlinearObjectiveFunction function, IEnumerable<Nonl /// problem.</param> /// <param name="function">The objective function to be optimized.</param> /// <param name="constraints"> - /// The <see cref="NonlinearConstraint"/>s to which the solution must be subjected.</param> + /// The <see cref="IConstraint"/>s to which the solution must be subjected.</param> /// public AugmentedLagrangian(IGradientOptimizationMethod innerSolver, - NonlinearObjectiveFunction function, IEnumerable<NonlinearConstraint> constraints) + NonlinearObjectiveFunction function, IEnumerable<IConstraint> constraints) : base(innerSolver.NumberOfVariables) { if (innerSolver.NumberOfVariables != function.NumberOfVariables) @@ -258,9 +258,9 @@ public AugmentedLagrangian(IGradientOptimizationMethod innerSolver, /// optimization method</see> used internally to solve the dual of this optimization /// problem.</param> /// <param name="constraints"> - /// The <see cref="NonlinearConstraint"/>s to which the solution must be subjected.</param> + /// The <see cref="IConstraint"/>s to which the solution must be subjected.</param> /// - public AugmentedLagrangian(IGradientOptimizationMethod innerSolver, IEnumerable<NonlinearConstraint> constraints) + public AugmentedLagrangian(IGradientOptimizationMethod innerSolver, IEnumerable<IConstraint> constraints) : base(innerSolver.NumberOfVariables) { init(null, constraints, innerSolver); @@ -268,7 +268,7 @@ public AugmentedLagrangian(IGradientOptimizationMethod innerSolver, IEnumerable< private void init(NonlinearObjectiveFunction function, - IEnumerable<NonlinearConstraint> constraints, IGradientOptimizationMethod innerSolver) + IEnumerable<IConstraint> constraints, IGradientOptimizationMethod innerSolver) { if (function != null) { @@ -294,9 +294,9 @@ private void init(NonlinearObjectiveFunction function, }; } - var equality = new List<NonlinearConstraint>(); - var lesserThan = new List<NonlinearConstraint>(); - var greaterThan = new List<NonlinearConstraint>(); + var equality = new List<IConstraint>(); + var lesserThan = new List<IConstraint>(); + var greaterThan = new List<IConstraint>(); foreach (var c in constraints) { diff --git a/Sources/Accord.Math/Optimization/Constrained/Constraints/ConstraintExtensions.cs b/Sources/Accord.Math/Optimization/Constrained/Constraints/ConstraintExtensions.cs new file mode 100644 index 0000000000..31fa0cea9 --- /dev/null +++ b/Sources/Accord.Math/Optimization/Constrained/Constraints/ConstraintExtensions.cs @@ -0,0 +1,79 @@ +// Accord Math Library +// The Accord.NET Framework +// http://accord-framework.net +// +// Copyright © César Souza, 2009-2017 +// cesarsouza at gmail.com +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +namespace Accord.Math.Optimization +{ + using System; + + /// <summary> + /// Extension methods on the <see cref="IConstraint"/> interface. + /// </summary> + public static class ConstraintExtensions + { + + /// <summary> + /// Gets how much the constraint is being violated. + /// </summary> + /// + /// <param name="input">The function point.</param> + /// + /// <returns> + /// How much the constraint is being violated at the given point. Positive + /// value means the constraint is not being violated with the returned slack, + /// while a negative value means the constraint is being violated by the returned + /// amount. + /// </returns> + /// + public static double GetViolation(this IConstraint constraint, double[] input) + { + double fx = constraint.Function(input); + + switch (constraint.ShouldBe) + { + case ConstraintType.EqualTo: + return Math.Abs(fx - constraint.Value); + + case ConstraintType.GreaterThanOrEqualTo: + return fx - constraint.Value; + + case ConstraintType.LesserThanOrEqualTo: + return constraint.Value - fx; + } + + throw new NotSupportedException(); + } + + /// <summary> + /// Gets whether this constraint is being violated + /// (within the current tolerance threshold). + /// </summary> + /// <param name="constraint">The constraint.</param> + /// <param name="input">The function point.</param> + /// <returns> + /// True if the constraint is being violated, false otherwise. + /// </returns> + public static bool IsViolated(this IConstraint constraint, double[] input) + { + return constraint.GetViolation(input) + constraint.Tolerance < 0; + } + } +} diff --git a/Sources/Accord.Math/Optimization/Constrained/Constraints/IConstraint.cs b/Sources/Accord.Math/Optimization/Constrained/Constraints/IConstraint.cs index d21330321..74f1167cd 100644 --- a/Sources/Accord.Math/Optimization/Constrained/Constraints/IConstraint.cs +++ b/Sources/Accord.Math/Optimization/Constrained/Constraints/IConstraint.cs @@ -23,11 +23,8 @@ namespace Accord.Math.Optimization { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - interface IConstraint + public interface IConstraint { /// <summary> /// Gets the type of the constraint. @@ -40,7 +37,13 @@ interface IConstraint /// side of the constraint equation. /// </summary> /// - double Value { get; } + double Value { get; } + + /// <summary> + /// Gets the violation tolerance for the constraint. + /// </summary> + /// + double Tolerance { get; } /// <summary> /// Gets the number of variables in the constraint. @@ -61,20 +64,5 @@ interface IConstraint /// </summary> /// Func<double[], double[]> Gradient { get; } - - /// <summary> - /// Gets how much the constraint is being violated. - /// </summary> - /// - /// <param name="input">The function point.</param> - /// - /// <returns> - /// How much the constraint is being violated at the given point. Positive - /// value means the constraint is not being violated with the returned slack, - /// while a negative value means the constraint is being violated by the returned - /// amount. - /// </returns> - /// - double GetViolation(double[] input); - } + } } diff --git a/Sources/Accord.Math/Optimization/Constrained/Constraints/LinearConstraint.cs b/Sources/Accord.Math/Optimization/Constrained/Constraints/LinearConstraint.cs index 56c0ca9b3..6c8316942 100644 --- a/Sources/Accord.Math/Optimization/Constrained/Constraints/LinearConstraint.cs +++ b/Sources/Accord.Math/Optimization/Constrained/Constraints/LinearConstraint.cs @@ -247,52 +247,6 @@ public LinearConstraint(IObjectiveFunction function, Expression<Func<bool>> cons this.Gradient = gradient; } - /// <summary> - /// Gets how much the constraint is being violated. - /// </summary> - /// - /// <param name="input">The function point.</param> - /// - /// <returns> - /// How much the constraint is being violated at the given point. Positive - /// value means the constraint is not being violated with the returned slack, - /// while a negative value means the constraint is being violated by the returned - /// amount. - /// </returns> - /// - public double GetViolation(double[] input) - { - double fx = compute(input); - - switch (ShouldBe) - { - case ConstraintType.EqualTo: - return Math.Abs(fx - Value); - - case ConstraintType.GreaterThanOrEqualTo: - return fx - Value; - - case ConstraintType.LesserThanOrEqualTo: - return Value - fx; - } - - throw new NotSupportedException(); - } - - /// <summary> - /// Gets whether this constraint is being violated - /// (within the current tolerance threshold). - /// </summary> - /// - /// <param name="input">The function point.</param> - /// - /// <returns>True if the constraint is being violated, false otherwise.</returns> - /// - public bool IsViolated(double[] input) - { - return GetViolation(input) < -Tolerance; - } - /// <summary> /// Attempts to create a <see cref="LinearConstraint"/> /// from a <see cref="System.String"/> representation. diff --git a/Sources/Accord.Math/Optimization/Constrained/Constraints/NonlinearConstraint.cs b/Sources/Accord.Math/Optimization/Constrained/Constraints/NonlinearConstraint.cs index ce2f2ad26..04b546ee0 100644 --- a/Sources/Accord.Math/Optimization/Constrained/Constraints/NonlinearConstraint.cs +++ b/Sources/Accord.Math/Optimization/Constrained/Constraints/NonlinearConstraint.cs @@ -56,53 +56,7 @@ public class NonlinearConstraint : IConstraint, IFormattable /// </summary> /// public Func<double[], double[]> Gradient { get; private set; } - - /// <summary> - /// Gets how much the constraint is being violated. - /// </summary> - /// - /// <param name="input">The function point.</param> - /// - /// <returns> - /// How much the constraint is being violated at the given point. Positive - /// value means the constraint is not being violated with the returned slack, - /// while a negative value means the constraint is being violated by the returned - /// amount. - /// </returns> - /// - public double GetViolation(double[] input) - { - double fx = Function(input); - - switch (ShouldBe) - { - case ConstraintType.EqualTo: - return Math.Abs(fx - Value); - - case ConstraintType.GreaterThanOrEqualTo: - return fx - Value; - - case ConstraintType.LesserThanOrEqualTo: - return Value - fx; - } - - throw new NotSupportedException(); - } - - /// <summary> - /// Gets whether this constraint is being violated - /// (within the current tolerance threshold). - /// </summary> - /// - /// <param name="input">The function point.</param> - /// - /// <returns>True if the constraint is being violated, false otherwise.</returns> - /// - public bool IsViolated(double[] input) - { - return GetViolation(input) < -Tolerance; - } - + /// <summary> /// Gets the type of the constraint. /// </summary> diff --git a/Unit Tests/Accord.Tests.Math/Optimization/AugmentedLagrangianTest.cs b/Unit Tests/Accord.Tests.Math/Optimization/AugmentedLagrangianTest.cs index 15df16c82..7c22f65f5 100644 --- a/Unit Tests/Accord.Tests.Math/Optimization/AugmentedLagrangianTest.cs +++ b/Unit Tests/Accord.Tests.Math/Optimization/AugmentedLagrangianTest.cs @@ -379,34 +379,19 @@ public void solve_vectors() gradient: (x) => new[] { 2 * (200 * Math.Pow(x[0], 3) - 200 * x[0] * x[1] + x[0] - 1), // df/dx = 2(200x³-200xy+x-1) - 200 * (x[1] - x[0]*x[0]) // df/dy = 200(y-x²) + 200 * (x[1] - x[0]*x[0]) // df/dy = 200(y-x²) } ); - // Now we can start stating the constraints - var constraints = new List<NonlinearConstraint>() - { - // Add the non-negativity constraint for x - new NonlinearConstraint(f, - // 1st constraint: x should be greater than or equal to 0 - function: (x) => x[0], // x - shouldBe: ConstraintType.GreaterThanOrEqualTo, - value: 0, - gradient: (x) => new[] { 1.0, 0.0 } - ), - - // Add the non-negativity constraint for y - new NonlinearConstraint(f, - // 2nd constraint: y should be greater than or equal to 0 - function: (x) => x[1], // y - shouldBe: ConstraintType.GreaterThanOrEqualTo, - value: 0, - gradient: (x) => new[] { 0.0, 1.0 } - ) - }; + // As before, we state the constraints. However, to illustrate the flexibility + // of the AugmentedLagrangian, we shall use LinearConstraints to constrain the problem. + double[,] a = Matrix.Identity(2); // Set up the constraint matrix... + double[] b = Vector.Zeros(2); // ...and the values they must be greater than + int numberOfEqualities = 0; + var linearConstraints = LinearConstraintCollection.Create(a, b, numberOfEqualities); // Finally, we create the non-linear programming solver - var solver = new AugmentedLagrangian(f, constraints); + var solver = new AugmentedLagrangian(f, linearConstraints); // And attempt to find a minimum bool success = solver.Minimize(); @@ -423,9 +408,9 @@ public void solve_vectors() Assert.AreEqual(1, solver.Solution[0], 1e-6); Assert.AreEqual(1, solver.Solution[1], 1e-6); - Assert.IsFalse(Double.IsNaN(minValue)); - Assert.IsFalse(Double.IsNaN(solver.Solution[0])); - Assert.IsFalse(Double.IsNaN(solver.Solution[1])); + Assert.IsFalse(double.IsNaN(minValue)); + Assert.IsFalse(double.IsNaN(solver.Solution[0])); + Assert.IsFalse(double.IsNaN(solver.Solution[1])); } [Test]