/// <summary> /// Find all theorems that hold true for the loose objects and the construction output of a given composed construction. /// </summary> /// <param name="composedConstruction">The composed construction to be examined.</param> /// <returns>The theorems holding true for the loose objects and the construction output.</returns> private IReadOnlyList <Theorem> FindTheoremsForComposedConstruction(ComposedConstruction composedConstruction) { // Local function that throws an exception void ThrowIncorrectConstructionException(string message, Exception innerException = null) // informing about the examination failure => throw new TheoremProverException($"Cannot examine construction {composedConstruction.Name}. {message}", innerException); // Create a helper constructed object that simulates the composed construction var helperConstructedObject = new ConstructedConfigurationObject(composedConstruction, // Its arguments will be the loose objects of the composed construction's configuration composedConstruction.Configuration.LooseObjects.Cast <ConfigurationObject>().ToArray()); // Prepare the configuration that simulates the composed construction var helperConfiguration = new Configuration(composedConstruction.Configuration.LooseObjectsHolder, new[] { helperConstructedObject }); // Safely execute var(pictures, data) = GeneralUtilities.TryExecute( // Construction of the new configuration () => _constructor.ConstructWithUniformLayout(helperConfiguration, NumberOfPicturesForFindingTrivialTheorems), // While making sure any exception is caught and re-thrown (InconsistentPicturesException e) => ThrowIncorrectConstructionException("The defining configuration couldn't be drawn.", e)); // Make sure it has no inconstructible objects if (data.InconstructibleObject != default) { ThrowIncorrectConstructionException("The defining configuration contains an inconstructible object."); } // Make sure it has no duplicates if (data.Duplicates != default) { ThrowIncorrectConstructionException("The defining configuration contains duplicate objects."); } // Safely execute var contextualPicture = GeneralUtilities.TryExecute( // Construction of the contextual picture () => new ContextualPicture(pictures), // While making sure any exception is caught and re-thrown (InconsistentPicturesException e) => ThrowIncorrectConstructionException("The contextual picture for the defining configuration couldn't be drawn.", e)); // Find the theorems return(_finder.FindAllTheorems(contextualPicture).AllObjects // Take those that say something about the last object // (there might be theorems in the layout as well, for example in RightTriangle) .Where(theorem => theorem.GetInnerConfigurationObjects().Contains(helperConstructedObject)) // We need to remap the helper constructed object to the one from the underlying configuration .Select(theorem => { // Prepare the mapping dictionary var mapping = new Dictionary <ConfigurationObject, ConfigurationObject> { // That already has the helper object remapped to the construction output { helperConstructedObject, composedConstruction.ConstructionOutput } }; // Add the loose objects to the mapping as identities composedConstruction.Configuration.LooseObjects.ForEach(looseObject => mapping.Add(looseObject, looseObject)); // Do the mapping return theorem.Remap(mapping); }) // Enumerate .ToList()); }
/// <inheritdoc/> public (TheoremMap initialTheorems, IEnumerable <ProblemGeneratorOutput> generationOutputs) Generate(ProblemGeneratorInput input) { #region Preparing variables // Prepare the stack for pictures of configurations that will be extended var picturesCache = new Stack <PicturesOfConfiguration>(); // Prepare the stack for contextual pictures of configurations that will be extended var contextualPictureCache = new Stack <ContextualPicture>(); // Prepare the stack for theorems of configurations that will be extended var theoremMapCache = new Stack <TheoremMap>(); #endregion #region Initial configuration // Safely execute var(initialPictures, initialData) = GeneralUtilities.TryExecute( // Constructing the configuration () => _constructor.ConstructWithUniformLayout(input.InitialConfiguration, _settings.NumberOfPictures), // Make sure a potential exception is caught and re-thrown (InconsistentPicturesException e) => throw new InitializationException("Drawing the initial configuration failed.", e)); // Make sure it is constructible if (initialData.InconstructibleObject != default) { throw new InitializationException($"The initial configuration contains an inconstructible object."); } // Make sure there are no duplicates... if (initialData.Duplicates != default) { throw new InitializationException($"The initial configuration contains duplicate objects."); } // Cache the pictures picturesCache.Push(initialPictures); // Safely execute var initialContextualPicture = GeneralUtilities.TryExecute( // Creating the contextual picture () => new ContextualPicture(initialPictures), // Make sure a potential exception is caught and re-thrown (InconsistentPicturesException e) => throw new InitializationException("Drawing the contextual container for the initial configuration failed.", e)); // Cache the contextual picture contextualPictureCache.Push(initialContextualPicture); // Find the initial theorems for the initial configuration var initialTheorems = _finder.FindAllTheorems(initialContextualPicture); // Cache the initial theorems theoremMapCache.Push(initialTheorems); #endregion #region Configuration verification function // Prepare a function that does geometric verification of a generated configuration // While doing so it construct pictures that we can reuse further for finding theorems bool VerifyConfigurationCorrectness(GeneratedConfiguration configuration) { #region Handling cache // We assume that the generator uses a memory-efficient DFS approach, i.e. // we need to remember only the last N-1 configurations, where N is the number // of the current iteration. // We got a configuration. We need to make sure that our cache contains only // objects belonging to the previous configurations of this. There final number // should be configuration.IterationIndex, therefore we know how many we should remove GeneralUtilities.ExecuteNTimes(picturesCache.Count - configuration.IterationIndex, () => { // Remove the last pictures / contextual picture / theorems map from the cache picturesCache.Pop(); contextualPictureCache.Pop(); theoremMapCache.Pop(); }); #endregion #region Exclusion based on symmetry // Find out if we should exclude this configuration because of symmetry // That can happen only if we are told to do so... var excludeBecauseOfSymmetry = input.SymmetryGenerationMode != SymmetryGenerationMode.GenerateBothSymmetricAndAsymmetric && // And it is not true that we can generate some objects that would make this configuration symmetric // To find all options that would do so, we need to distinguish the symmetry mode !(input.SymmetryGenerationMode switch { // Standard symmetry yields a list of options (i.e. object lists) SymmetryGenerationMode.GenerateOnlySymmetric => configuration.GetObjectsThatWouldMakeThisConfigurationSymmetric(), // Full symmetry yields only one object list SymmetryGenerationMode.GenerateOnlyFullySymmetric => configuration.GetObjectsThatWouldMakeThisConfigurationFullySymmetric().ToEnumerable(), // Unhandled cases _ => throw new GeoGenException($"Unhandled value of {nameof(SymmetryGenerationMode)}: {input.SymmetryGenerationMode}"), }) // Find out if given objects to be added could really be added .Any(objectsToBeAdded => { // If we don't have enough iterations to add that many objects, then we're done if (configuration.IterationIndex + objectsToBeAdded.Count > input.NumberOfIterations) { return(false); } // Otherwise find out if we have the needed constructions by taking the objects var doWeHaveNeededConstructions = objectsToBeAdded // Their constructions .Select(objectToBeAdded => objectToBeAdded.Construction) // All of them must be among the ones we're using to extend objects .All(input.Constructions.Contains); // If we don't have the needed constructions, these objects can't be generated if (!doWeHaveNeededConstructions) { return(false); } // Otherwise categorize objects to be added by taking them return(objectsToBeAdded // Grouping by their type .GroupBy(objectToBeAdded => objectToBeAdded.ObjectType) // It needs to be true that we can add that many objects from every type .All(group => { // Get the type var type = group.Key; // Get the needed count var neededObjectsOfThisTypeCount = group.Count(); // Get the number of already added objects of this type by taking all objects of this type var addedObjectsOfThisTypeCount = configuration.ObjectMap.GetObjectsForKeys(type).Count() // And subtracting objects of this type from the initial configuration - input.InitialConfiguration.ObjectMap.GetObjectsForKeys(type).Count(); // We can add these objects if and only if we will not exceed the maximal number of objects to add return neededObjectsOfThisTypeCount + addedObjectsOfThisTypeCount <= input.MaximalNumbersOfObjectsToAdd[type]; })); }); // If we should exclude this configuration because of symmetry, do it if (excludeBecauseOfSymmetry) { return(false); } #endregion #region Pictures construction // Get the previous pictures var previousPictures = picturesCache.Peek(); // Safely execute var(newPictures, constructionData) = GeneralUtilities.TryExecute( // Constructing the pictures for the configuration () => _constructor.ConstructByCloning(previousPictures, configuration), // While tracing a possible failure (such configurations will be discarded in the next step) (InconsistentPicturesException e) => _tracer.InconstructiblePicturesByCloning(previousPictures, configuration, e)); // The configuration is incorrect if it couldn't be carried out... var isConfigurationIncorrect = newPictures == default // Or if it contains an inconstructible object || constructionData.InconstructibleObject != default // Or if it contains the same object twice || constructionData.Duplicates != default; // Exclude incorrect configuration if (isConfigurationIncorrect) { return(false); } // We have to remember the pictures for further use picturesCache.Push(newPictures); #endregion #region Contextual picture construction // Get the previous contextual picture var previousContextualPicture = contextualPictureCache.Peek(); // Safely execute var newContextualPicture = GeneralUtilities.TryExecute( // Constructing the new contextual picture by cloning () => previousContextualPicture.ConstructByCloning(newPictures), // While tracing a possible failure (such configurations will be discarded in the next step) (InconsistentPicturesException e) => _tracer.InconstructibleContextualPictureByCloning(previousContextualPicture, newPictures, e)); // If the construction of the picture cannot be done... if (newContextualPicture == default) { // We need to make sure the already constructed pictures are discarded picturesCache.Pop(); // And we say the configuration is incorrect return(false); } // We have to remember the contextual picture for further use contextualPictureCache.Push(newContextualPicture); #endregion // If we got here, everything's fine return(true); }