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()
// 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)); }