Example #1
0
        public InternalSolution Solve()
        {
            // first thing's first!
            if (!IsStandardized)
            {
                try
                {
                    Standardize();
                }
                catch
                {
                    var effff = SolutionHelper.Infeasible();
                    return(new InternalSolution(effff, this));
                }
            }

            // Loop over finding new basic variables until terminating conditions found
            for (var badUniqueVariableName = 0; badUniqueVariableName < 300; badUniqueVariableName++)
            {
                // create matrix of basics
                var basicInverse = DenseMatrix.OfColumnVectors(PColumns.Where(p => p.IsBasic).ToArray()).Inverse();

                // compute product of inverse and RHS values
                var xPrime = DenseVector.OfEnumerable((basicInverse * (XColumn.ToColumnMatrix())).ToColumnWiseArray().Select(x => Math.Round(x, 6)));


                var pPrimes = new List <PVectorPrime>();
                var zCoefs  = DenseVector.OfEnumerable(PColumns.Where(p => p.IsBasic).Select(p => p.ZValue));
                foreach (var col in PColumns.Where(p => !p.IsBasic))
                {
                    //was getting some weird precision issues... so chop 'em short
                    var pPrime = DenseVector.OfEnumerable((basicInverse * col.ToColumnMatrix()).ToColumnWiseArray().Select(x => Math.Round(x, 6)));
                    var cPrime = Math.Round(col.ZValue - (zCoefs * pPrime), 6);

                    pPrimes.Add(new PVectorPrime(col, pPrime, cPrime));
                }

                //var anyNegative = cPrimes.Any(x => x < 0);
                var anyNegative = pPrimes.Any(x => Math.Round(x.CPrime, 2) < 0);
                if (anyNegative)
                {
                    PVector entering, exiting = null;


                    // determine entering basic variable (using Bland's method. inefficient, but reliable)
                    var tempPrime = pPrimes.First(p => Math.Round(p.CPrime, 4) < 0);
                    entering = tempPrime.PColumn;

                    // compute ratio of x prime to p prime assosiated with new basic variable
                    var ratios = (xPrime / tempPrime.PColumnPrime).Select(x => Math.Round(x, 6)).ToList();

                    // make sure there are positives, if not, then the entire problem unbounded
                    var anyPositive = ratios.Any(x => x > 0 && !double.IsInfinity(x));
                    if (anyPositive)
                    {
                        var min = ratios.Min(x => x > 0 ? x : double.PositiveInfinity);

                        var minIndexes = Enumerable.Range(0, ratios.Count).Where(i => ratios[i] == min).ToArray();
                        if (minIndexes.Length > 1)
                        {
                            var potentialExits = PColumns.Where(p => p.IsBasic).Where((x, i) => minIndexes.Contains(i));
                            var artificalCount = PColumns.Count(p => p.IsArtificial);

                            exiting = potentialExits.OrderByDescending(p => PColumns.Skip(PColumns.Count - artificalCount).Take(artificalCount - 1).Contains(p)).First();
                        }
                        else
                        {
                            exiting = PColumns.Where(p => p.IsBasic).ElementAt(minIndexes[0]);
                        }
                    }
                    else
                    {
                        var anyPositiveOrZeros = ratios.Any(x => x >= 0 && !double.IsInfinity(x));
                        if (anyPositiveOrZeros)
                        {
                            var min = ratios.Min(x => x >= 0 ? x : double.PositiveInfinity);

                            var minIndexes = Enumerable.Range(0, ratios.Count).Where(i => ratios[i] == min).ToArray();
                            if (minIndexes.Length > 1)
                            {
                                var potentialExits = PColumns.Where(p => p.IsBasic).Where((x, i) => minIndexes.Contains(i));
                                var artificalCount = PColumns.Count(p => p.IsArtificial);

                                exiting = potentialExits.OrderByDescending(p => PColumns.Skip(PColumns.Count - artificalCount).Take(artificalCount - 1).Contains(p)).First();
                            }
                            else
                            {
                                exiting = PColumns.Where(p => p.IsBasic).ElementAt(minIndexes[0]);
                            }
                        }
                        else
                        {
                            return(new InternalSolution(SolutionHelper.Unbounded()));
                        }
                    }

                    // actually change the basic-ness
                    entering.IsBasic = true;
                    exiting.IsBasic  = false;
                }
                // infeasible
                else if (PColumns.Any(p => p.IsArtificial && p.IsBasic))
                {
                    return(new InternalSolution(SolutionHelper.Infeasible(), this));
                }
                // optimal
                else
                {
                    var targetNumDecisions = Goal.Coefficients.Length;

                    var decisions = new List <double>();
                    var xIndex    = 0;
                    foreach (var p in PColumns.Where(x => x.IsDecision))
                    {
                        if (p.IsBasic)
                        {
                            decisions.Add(xPrime[xIndex]);
                            xIndex++;
                        }
                        else
                        {
                            decisions.Add(0);
                        }
                    }
                    var value = decisions.Select((x, i) => x * Goal.Coefficients[i]).Sum();
                    value += Goal.ConstantTerm;

                    var allPPrimes = new List <PVectorPrime>();
                    foreach (var col in PColumns)
                    {
                        //was getting some weird precision issues... so chop 'em short
                        var pPrime = DenseVector.OfEnumerable((basicInverse * col.ToColumnMatrix()).ToColumnWiseArray());
                        var cPrime = Math.Round(col.ZValue - (zCoefs * pPrime), 6);

                        allPPrimes.Add(new PVectorPrime(col, pPrime, cPrime));
                    }
                    var infiniteSols = pPrimes.Count(p => Math.Round(p.CPrime, 4) == 0) >= Goal.Coefficients.Length;

                    var sol = SolutionHelper.Optimal(decisions.ToArray(), value, infiniteSols);
                    return(new InternalSolution(sol, this));
                }
            }
            var uhoh = SolutionHelper.TimedOut();

            return(new InternalSolution(uhoh, this));
        }
Example #2
0
        private void Standardize()
        {
            // minimize -> maximize
            var stdGoalCoefs = (double[])Goal.Coefficients.Clone();

            if (GoalKind == GoalKind.Maximize)
            {
                stdGoalCoefs = stdGoalCoefs.Select(x => x *= -1).ToArray();
            }

            // add goal as constraint if two-phase
            if (IsTwoPhase)
            {
                Constraints.Add(new LinearConstraint()
                {
                    Coefficients = stdGoalCoefs,
                    Relationship = Relationship.Equals,
                    Value        = 0
                });
            }

            // determine number of coefficients
            var pCount = Constraints[0].Coefficients.Length;

            if (!Constraints.TrueForAll(c => c.Coefficients.Length == pCount))
            {
                throw new Exception("You dirty wanker, all your linear Constraints need to have the same number of coeffecients!");
            }
            if (Goal.Coefficients.Length != pCount)
            {
                throw new Exception("You stinky twat, you need to have the same number of coefficients in your goal as you do in your linear Constraints!");
            }

            // create list of slack/surplus/artifical variable columns
            var sColumns = new List <PVector>();
            var aColumns = new List <PVector>();

            // add vectors of constraint coefficients, marked as non-basic and decision
            for (var i = 0; i < pCount; i++)
            {
                PColumns.Add(new PVector(DenseVector.OfEnumerable(Constraints.Select(c => c.Coefficients[i]).ToArray()), false, true, false));
            }

            // create surplus/slack/artificial variables for each constraint and determine indexes of basic
            for (int i = 0; i < Constraints.Count; i++)
            {
                var rel = Constraints[i].Relationship;

                // create surplus/slack variable column for <= and => relationships
                // will be basic if <=
                if (rel != Relationship.Equals)
                {
                    var vect = DenseVector.Create(Constraints.Count, x => x = 0);
                    vect[i] = rel == Relationship.LessThanOrEquals ? 1 : -1;
                    sColumns.Add(new PVector(vect, rel == Relationship.LessThanOrEquals, false, false));
                }

                // add artifical variable column for == and => relationships
                // always basic
                if (rel != Relationship.LessThanOrEquals)
                {
                    var vect = DenseVector.Create(Constraints.Count, x => x = 0);
                    vect[i] = 1;
                    aColumns.Add(new PVector(vect, true, false, true));
                }
            }

            // combine PColumns SColumns and AColumns into PColumns
            PColumns.AddRange(sColumns);
            PColumns.AddRange(aColumns);

            // mark z column as not artificial, move to front
            if (IsTwoPhase)
            {
                PColumns.Last().IsArtificial = false;
                PColumns.Insert(0, PColumns.Last());
                PColumns.RemoveAt(PColumns.Count - 1);
            }

            // create ZRow
            var zRow = DenseVector.Create(PColumns.Count, x => x = 0);

            if (IsTwoPhase)
            {
                // add up and negate coefficient values for temp objective function
                // look at all rows containing an artificial variable (except for the last one, which is for the old z)
                foreach (var a in aColumns.Take(aColumns.Count - 1))
                {
                    //super jank
                    zRow += DenseVector.OfEnumerable(PColumns.Take(PColumns.Count - aColumns.Count + 1).Select(v => v[a.MaximumIndex()]).Concat(new double[aColumns.Count - 1]));
                }
                zRow *= -1;
                zRow.At(0, 0);
            }
            else
            {
                // create ZRow vector of standardized goal
                zRow = DenseVector.OfEnumerable(stdGoalCoefs.Concat(new double[PColumns.Count - stdGoalCoefs.Length]));
            }

            //add zRow values to pColumns
            for (int i = 0; i < zRow.Count; i++)
            {
                PColumns[i].ZValue = zRow[i];
            }

            // create XColumn vector of RHS values
            XColumn = DenseVector.OfEnumerable(Constraints.Select(c => c.Value));
            // add extra term for two-phase
            if (IsTwoPhase)
            {
                XColumn[XColumn.Count - 1] = Goal.ConstantTerm;
            }

            IsStandardized = true;
        }