public bool CreateFile(uint cVars, uint cConstraintsPerVar, string strOutFile)
        {
            Debug.Assert(cVars > 0, "Test file creation requires cVars > 0");
            Debug.Assert(0 != cConstraintsPerVar, "Test file creation requires cConstraintsPerVar > 0");

            // Generate a new set of Variable and Constraint definitions.
            var lstVarDefs = new List <VariableDef>();
            var rng        = TestConstraints.NewRng();

            // Print this so that in case of errors we can re-run with this seed.
            Console.WriteLine("Creating test file with seed 0x{0}", TestConstraints.RandomSeed.ToString("X"));

            //
            // This code is adapted and extended from satisfy_inc.
            //
            for (int idxVar = 0; idxVar < cVars; ++idxVar)
            {
                // Assign initial variable positions in the range [0..TestConstraints.MaxNodePosition].
                double dblPos    = WantStartAtZero ? 0.0 : TestConstraints.RoundRand(rng, TestConstraints.MaxNodePosition);
                double dblWeight = 1.0;
                if (TestConstraints.MaxWeightToGenerate > 0.0)
                {
                    // Ensure nonzero weight.
                    dblWeight = TestConstraints.RoundRand(rng, TestConstraints.MaxWeightToGenerate);
                    if (dblWeight < 0.01)
                    {
                        dblWeight = 0.01;
                    }
                    Debug.Assert(dblWeight >= 0.01, "Random variable weight assignment is less than expected");
                }
                lstVarDefs.Add(new VariableDef((uint)idxVar, dblPos, ValueNotUsed, dblWeight));
            } // endfor idxVar

            var lstCstDefs = new List <ConstraintDef>();

            // Create constraints outgoing from every variable except the final one.
            for (int idxLhs = 0; idxLhs < (cVars - 1); ++idxLhs)
            {
                // rng.Next returns a value in [0..arg-1], so add 1.
                int cCurCst = rng.Next((int)cConstraintsPerVar) + 1;

                // Randomly make an equality constraint if the lhs variable is not currently in such
                // a constraint (avoid transitivity, which may lead to impossible-to-satisfy conditions;
                // checking lhs is sufficient as we'll only create equality to the lhs+1 variable).
                int idxCst   = 0;       // Current index from 0 .. cCurCst-1
                int iRhsBump = 1;       // Will be used to skip the immediate-next if we've created an == cst to it
                if (WantEqualityConstraints && !lstVarDefs[idxLhs].IsInEqualityConstraint)
                {
                    // Test one (mid-digit) bit for about a 50% likelihood, less the likelihood of lhs being
                    // the rhs of an equality constraint from lhs-1; we want flexibility between equality
                    // constraints so we don't have unsatisfiable cases like
                    //      a + 3 == b; b + 3 == c; a + 9 <= c
                    const int Mask = 0x10;
                    if ((rng.Next() & Mask) == Mask)
                    {
                        double dblGap = TestConstraints.RoundRand(rng, MaxGapToGenerate);
                        lstCstDefs.Add(new ConstraintDef(lstVarDefs[idxLhs], lstVarDefs[idxLhs + 1], dblGap, true /*fEquality*/));
                        if ((cVars - 2) == idxLhs)
                        {
                            // This was the next-to-last variable and we've made an equality constraint
                            // to the next one, so don't make any more constraints for this var.
                            continue;
                        }
                    }
                    ++idxCst;
                    ++iRhsBump;
                }

                for (; idxCst < cCurCst; ++idxCst)
                {
                    // Create a constraint from the current idxLhs (lhs) to a randomly-determined
                    // rhs variable that has an ordinal higher than lhs.  This ensures no constraint
                    // cycles, with the number and length of chains depending upon cConstraintsPerVar.
                    int idxRhs = rng.Next(idxLhs, (int)cVars - iRhsBump) + iRhsBump;
                    idxRhs = (int)Math.Min(idxRhs, cVars - 1);

                    double dblGap = TestConstraints.RoundRand(rng, MaxGapToGenerate);
                    lstCstDefs.Add(new ConstraintDef(lstVarDefs[idxLhs], lstVarDefs[idxRhs], dblGap, false /*fEquality*/));
                }
            } // endfor create non-cyclic constraints

            // Create cyclic constraints if requested by creating constraints from higher indexes to lower.
            // Do this randomly so that chains of varying length are created; some reverse constraints may
            // be harmless but most should create cycles.
            for (int idxCst = 0; idxCst < NumberOfCyclesToCreate; ++idxCst)
            {
                int    idxLhs = rng.Next((int)cVars - 1);
                int    idxRhs = rng.Next(idxLhs, (int)cVars - 1) + 1;
                double dblGap = TestConstraints.RoundRand(rng, MaxGapToGenerate);

                // Create in reverse direction.
                lstCstDefs.Add(new ConstraintDef(lstVarDefs[idxRhs], lstVarDefs[idxLhs], dblGap, false /*fEquality*/));
            }

            var lstNbourDefs = new List <NeighborDef>();

            if (NumberOfNeighboursPerVar > 0)
            {
                // Create neighbours outgoing from every variable except the final one.
                for (int idxLhs = 0; idxLhs < (cVars - 1); ++idxLhs)
                {
                    // rng.Next returns a value in [0..arg-1], so add 1.
                    int cCurNbour = rng.Next(NumberOfNeighboursPerVar) + 1;
                    for (uint idxNbour = 0; idxNbour < cCurNbour; ++idxNbour)
                    {
                        // Create a neighbour from the current idxLhs (lhs) to a randomly-determined
                        // rhs variable that has an ordinal higher than lhs.  This ensures no neighbour
                        // cycles, with the number and length of chains depending upon cNeighboursPerVar.
                        int idxRhs = rng.Next(idxLhs, (int)cVars - 1) + 1;

                        double dblWeight = 1.0;
                        if (TestConstraints.MaxWeightToGenerate > 0.0)
                        {
                            // Create the neighbour relationship with random non-zero weight.
                            dblWeight = TestConstraints.RoundRand(rng, TestConstraints.MaxWeightToGenerate);
                            if (dblWeight < 0.01)
                            {
                                dblWeight = 0.01;
                            }
                            Debug.Assert(dblWeight >= 0.01, "Random neighbour weight assignment is less than expected");
                        }
                        lstNbourDefs.Add(new NeighborDef(lstVarDefs[idxLhs], lstVarDefs[idxRhs], dblWeight));
                    }
                } // endfor create neighbours
            }     // endif create neighbours

            // Do variable scale last; scaling was added after files were generated so we don't want to
            // affect the rng sequence before this.  These can end up very small in qpsc so we'll want
            // to support creating them much smaller than weight.
            if (0.0 != MaxScaleToGenerate)
            {
                foreach (var varDef in lstVarDefs)
                {
                    // Ensure nonzero scale.
                    varDef.ScaleX = Math.Max(TestConstraints.RoundRand(rng, MaxScaleToGenerate), 1e-6);
                }
            }

            return(WriteTestFile(lstVarDefs, lstCstDefs, lstNbourDefs, strOutFile));
        } // end CreateFile()
Esempio n. 2
0
        // Note: cConstraintsPerVar is an artifact of ProjectionSolver testing and is ignored
        // in OverlapRemoval since we generate only the necessary constraints for removing overlaps (it
        // could be extended to generate additional overlaps but that has not yet been done).
        public bool CreateFile(uint cVars, uint cConstraintsPerVar, string strOutFile)
        {
            Validate.IsTrue(cVars > 0, "Test file creation requires cVars > 0");
            Validate.AreNotEqual((uint)0, cConstraintsPerVar, "Test file creation requires cConstraintsPerVar > 0");

            // Generate a new set of Variable definitions.
            var    lstVarDefs = new List <VariableDef>();
            Random rng        = TestConstraints.NewRng();

            // Print this so that in case of errors we can re-run with this seed.
            Console.WriteLine("Creating test file with seed 0x{0}", TestConstraints.RandomSeed.ToString("X"));

            //
            // This code was adapted from satisfy_inc for ProjSolver, and then the second dimension
            // and sizes were added for OverlapRemoval, as well as other extensions.
            //

            for (int idxVar = 0; idxVar < cVars; ++idxVar)
            {
                double dblPosX = TestConstraints.RoundRand(rng, TestConstraints.MaxNodePosition);
                double dblPosY = TestConstraints.RoundRand(rng, TestConstraints.MaxNodePosition);

                // Ensure nonzero sizes.
                double dblSizeX = TestConstraints.RoundRand(rng, MaxSize) + 1.0;
                double dblSizeY = TestConstraints.RoundRand(rng, MaxSize) + 1.0;

                double dblWeightX = 1.0, dblWeightY = 1.0;
                if (TestConstraints.MaxWeightToGenerate > 0.0)
                {
                    // Ensure nonzero weights.
                    dblWeightX = TestConstraints.RoundRand(rng, TestConstraints.MaxWeightToGenerate) + 0.01;
                    dblWeightY = TestConstraints.RoundRand(rng, TestConstraints.MaxWeightToGenerate) + 0.01;
                }

                lstVarDefs.Add(new VariableDef((uint)idxVar
                                               , dblPosX, dblPosY
                                               , dblSizeX, dblSizeY
                                               , dblWeightX, dblWeightY));
            } // endfor idxVar

            List <ClusterDef> lstClusDefs = null;

            if (MaxClusters > 0)
            {
                // If we are generating a random number of clusters, get that number here.
                int cClusters = MaxClusters;
                if (WantRandomClusters)
                {
                    cClusters = rng.Next(cClusters);
                }

                // Add the first cluster, at the root level - hence no parent and no borders.
                // No BorderInfo needed for root clusters - it's ignored.  RoundRand returns
                // 0 if its arg is 0.
                lstClusDefs = new List <ClusterDef>(cClusters)
                {
                    new ClusterDef(
                        TestConstraints.RoundRand(rng, MinClusterSizeX),
                        TestConstraints.RoundRand(rng, MinClusterSizeY))
                    {
                        IsNewHierarchy = true
                    }
                };
                if (TestGlobals.VerboseLevel >= 3)
                {
                    Console.WriteLine("Level-1 cluster: {0}", lstClusDefs[0].ClusterId);
                }

                // If we are doing a single hierarchy only, restrict the range to the current set of parents,
                // otherwise ourselves about a 10% chance of being at the root level instead of being nested.
                int cRootExtra = WantSingleClusterRoot ? 0 : Math.Max(cClusters / 10, 1);

                // Create the clusters, randomly selecting a parent for each from the items previously
                // put in the list.
                for (int idxNewClus = 1; idxNewClus < cClusters; ++idxNewClus)
                {
                    int idxParentClus = rng.Next(lstClusDefs.Count + cRootExtra);   // Allow out-of-bounds index as "root level" flag

                    // Margin stuff stays 0 if MaxMargin is 0 (and border stuff is ignored if it's a root cluster).
                    var clusNew = new ClusterDef(TestConstraints.RoundRand(rng, MinClusterSizeX), TestConstraints.RoundRand(rng, MinClusterSizeY),
                                                 GetBorderInfo(lstClusDefs.Count, TestConstraints.RoundRand(rng, MaxMargin), WantFixedLeftBorder),
                                                 GetBorderInfo(lstClusDefs.Count, TestConstraints.RoundRand(rng, MaxMargin), WantFixedRightBorder),
                                                 GetBorderInfo(lstClusDefs.Count, TestConstraints.RoundRand(rng, MaxMargin), WantFixedTopBorder),
                                                 GetBorderInfo(lstClusDefs.Count, TestConstraints.RoundRand(rng, MaxMargin), WantFixedBottomBorder));
                    lstClusDefs.Add(clusNew);

                    // If we are doing a single hierarchy only, restrict the range to the current set of parents.
                    // Otherwise, if the parent index is >= the index we're adding now, that's our way of
                    // selecting the node to be at the root level.
                    if (WantSingleClusterRoot)
                    {
                        idxParentClus %= idxNewClus;
                    }
                    if (idxParentClus < idxNewClus)
                    {
                        ClusterDef clusParent = lstClusDefs[idxParentClus];
                        clusParent.AddClusterDef(clusNew);
                        if (TestGlobals.VerboseLevel >= 3)
                        {
                            Console.Write("Nested cluster: {0}", clusNew.ClusterId);
                            for (; null != clusParent; clusParent = clusParent.ParentClusterDef)
                            {
                                Console.Write(" {0}", clusParent.ClusterId);
                            }
                            Console.WriteLine();
                        }
                    }
                    else
                    {
                        // Create a simple cluster since root clusters don't honor borders.
                        clusNew.IsNewHierarchy = true;
                        if (TestGlobals.VerboseLevel >= 3)
                        {
                            Console.WriteLine("Level-1 cluster: {0}", clusNew.ClusterId);
                        }
                    }
                }

                // Now run through the nodes and randomly assign them into the clusters.
                foreach (VariableDef varDef in lstVarDefs)
                {
                    int idxParentClus = rng.Next(lstClusDefs.Count + cRootExtra);
                    if (idxParentClus < lstClusDefs.Count)
                    {                        // Don't write the ones at the root level.
                        lstClusDefs[idxParentClus].AddVariableDef(varDef);
                    }
                    else if (TestGlobals.VerboseLevel >= 3)
                    {
                        Console.WriteLine("Root var: {0}", varDef.IdString);
                    }
                }
            } // endif MaxClusters > 0

            return(WriteTestFile(lstVarDefs, lstClusDefs, strOutFile));
        }