/// <inheritdoc/> public void TraceInconstructibleTheorem(RankedTheorem rankedTheorem, AnalyticException exception) { // If logging is allowed, log it with the reference to more detail in the file if (_settings.LogFailures) { Log.Warning("Problem while drawing a ranked theorem. See {path} for more detail.", _settings.FailureFilePath); } // Open the stream writer for the file using var streamWriter = new StreamWriter(_settings.FailureFilePath, append: true); // Prepare the formatter var rankedTheoremFormatter = new OutputFormatter(rankedTheorem.Configuration.AllObjects); // Write initial info streamWriter.WriteLine($"Problem while constructing the theorem:\n"); // Write the configuration streamWriter.WriteLine(rankedTheoremFormatter.FormatConfiguration(rankedTheorem.Configuration)); // Write the theorem streamWriter.WriteLine($"\n{rankedTheoremFormatter.FormatTheorem(rankedTheorem.Theorem)}"); // Write the exception streamWriter.WriteLine($"\nException: {exception}"); // Separator streamWriter.WriteLine("--------------------------------------------------\n"); }
/// <summary> /// Converts given ranked theorems to a string. /// </summary> /// <param name="rankedTheorems">The ranked theorems to be converted.</param> /// <returns>The string representing the ranked theorems.</returns> private static string RankedTheoremsToString(IEnumerable <RankedTheorem> rankedTheorems) // Go through the theorems => rankedTheorems.Select((rankedTheorem, index) => { // Prepare the formatter of the configuration var formatter = new OutputFormatter(rankedTheorem.Configuration.AllObjects); // Prepare the header var header = $"Theorem {index + 1}"; // Prepare the result where the header is framed in dashes var result = $"{new string('-', header.Length)}\n{header}\n{new string('-', header.Length)}\n\n"; // Add the configuration result += formatter.FormatConfiguration(rankedTheorem.Configuration); // Add the theorem result += $"\n\n{formatter.FormatTheorem(rankedTheorem.Theorem)}" + // Add the total ranking $" - total ranking {rankedTheorem.Ranking.TotalRanking.ToStringWithDecimalDot()}\n\n"; // Add the ranking result += TheoremRankingToString(rankedTheorem.Ranking); // Finally return the result return(result); }) // Make each on a separate line .ToJoinedString("\n\n");
/// <summary> /// Converts an original <see cref="RankedTheorem"/> object into an intermediate object to be serialized. /// </summary> /// <param name="rankedTheorem">The object to be converted.</param> /// <returns>The result of the conversion.</returns> public static RankedTheoremIntermediate Convert(RankedTheorem rankedTheorem) { // Prepare the formatter for the configuration var formatter = new OutputFormatter(rankedTheorem.Configuration.AllObjects); // Format the configuration var configurationString = formatter.FormatConfiguration(rankedTheorem.Configuration) // Replace all curly braces that are not supported by the parser (and don't matter after all) .Replace("{", "").Replace("}", ""); // Format the theorem var theoremString = formatter.FormatTheorem(rankedTheorem.Theorem) // Replace all curly braces that are not supported by the parser (and don't matter after all) .Replace("{", "").Replace("}", ""); // Return the final object return(new RankedTheoremIntermediate(theoremString, rankedTheorem.Ranking, configurationString)); }
/// <inheritdoc/> public void MarkInvalidInferrence(Configuration configuration, Theorem invalidConclusion, InferenceRule inferenceRule, Theorem[] negativeAssumptions, Theorem[] possitiveAssumptions) { // Prepare the file path for the rule with the name of the rule var filePath = Path.Combine(_settings.InvalidInferenceFolder, $"{inferenceRule.ToString().Replace(Path.DirectorySeparatorChar, '_')}.{_settings.FileExtension}"); // If adding this inference would reach the maximal number of written inferences, we're done if (_invalidInferencesPerFile.GetValueOrDefault(filePath) + 1 > _settings.MaximalNumberOfInvalidInferencesPerFile) { return; } // Otherwise create or get the file in the invalid inference folder using var writer = new StreamWriter(filePath, append: true); // Prepare the formatter of the configuration var formatter = new OutputFormatter(configuration.AllObjects); // Write the configuration writer.WriteLine(formatter.FormatConfiguration(configuration)); // An empty line writer.WriteLine(); // Write the incorrect theorem writer.WriteLine($" {formatter.FormatTheorem(invalidConclusion)}"); // Write its assumptions possitiveAssumptions.ForEach(assumption => writer.WriteLine($" - {formatter.FormatTheorem(assumption)}")); // As well as negative ones negativeAssumptions.ForEach(assumption => writer.WriteLine($" ! {formatter.FormatTheorem(assumption)}")); // Separator writer.WriteLine("--------------------------------------------------\n"); // Mark that we've used this inference _invalidInferencesPerFile[filePath] = _invalidInferencesPerFile.GetValueOrDefault(filePath) + 1; }
/// <summary> /// Traces that a given contextual picture couldn't be cloned and extended with the new object /// already drawn in pictures representing some configuration. /// </summary> /// <param name="previousContextualPicture">The contextual picture that was correct and failed to add the new object.</param> /// <param name="newConfigurationPictures">The pictures holding geometry data of the new object that was added.</param> /// <param name="exception">The inner inconsistency exception that caused the issue.</param> public void InconstructibleContextualPictureByCloning(ContextualPicture previousContextualPicture, PicturesOfConfiguration newConfigurationPictures, InconsistentPicturesException exception) { // Prepare the initial information string var infoString = $"Undrawable object into a contextual picture."; // If logging is allowed, log it with the reference to more detail in the file if (_settings.LogFailures) { Log.Warning("Object generation: {info} See {path} for more detail.", infoString, _settings.FailureFilePath); } // Prepare the formatter for the configuration var formatter = new OutputFormatter(newConfigurationPictures.Configuration.AllObjects); // Add the data about how the object can be drawn infoString += $"\n\nThe object is the last object of the following defining configuration:\n\n{formatter.FormatConfiguration(newConfigurationPictures.Configuration).Indent(2)}"; // Add the exception infoString += $"\n\nThe details of the exception: {exception.Format(formatter)}\n"; // Open the stream writer for the file using var streamWriter = new StreamWriter(_settings.FailureFilePath, append: true); // Write indented message to the file streamWriter.WriteLine($"- {infoString.Indent(3).TrimStart()}"); }
/// <summary> /// The entry method of the application. /// </summary> /// <param name="arguments">The three arguments: /// <list type="number"> /// <item>Path to the inference rule folder.</item> /// <item>The extension of the inference rule files.</item> /// <item>Path to the object introduction rule file.</item> /// </list> /// </param> private static async Task Main(string[] arguments) { #region Kernel preparation // Prepare the settings for the inference rule provider var inferenceRuleProviderSettings = new InferenceRuleProviderSettings(ruleFolderPath: arguments[0], fileExtension: arguments[1]); // Prepare the settings for the object introduction rule provider var objectIntroductionRuleProviderSettings = new ObjectIntroductionRuleProviderSettings(filePath: arguments[2]); // Prepare the kernel var kernel = Infrastructure.NinjectUtilities.CreateKernel() // That constructors configurations .AddConstructor() // That can find theorems .AddTheoremFinder(new TheoremFindingSettings ( // Look for theorems of any type soughtTheoremTypes: Enum.GetValues(typeof(TheoremType)).Cast <TheoremType>() // Except for the EqualObjects that don't have a finder .Except(TheoremType.EqualObjects.ToEnumerable()) // Enumerate .ToArray(), // Exclude in-picture tangencies new TangentCirclesTheoremFinderSettings(excludeTangencyInsidePicture: true), new LineTangentToCircleTheoremFinderSettings(excludeTangencyInsidePicture: true) )) // That can prove theorems .AddTheoremProver(new TheoremProvingSettings ( // Use the provider to find the inference rules new InferenceRuleManagerData(await new InferenceRuleProvider.InferenceRuleProvider(inferenceRuleProviderSettings).GetInferenceRulesAsync()), // Use the provider to find the object introduction rules new ObjectIntroducerData(await new ObjectIntroductionRuleProvider.ObjectIntroductionRuleProvider(objectIntroductionRuleProviderSettings).GetObjectIntroductionRulesAsync()), // Setup the prover new TheoremProverSettings ( // We will be strict and don't assume simplifiable theorems assumeThatSimplifiableTheoremsAreTrue: false, // We will find trivial theorems for all objects findTrivialTheoremsOnlyForLastObject: false ) )); #endregion #region Tests // Take the tests new[] { PerpendicularBisectorsAreConcurrent(), IncenterAndTangentLine(), Midpoints(), Parallelogram(), HiddenExcenter(), HiddenMidpoint(), LineTangentToCircle(), ConcurrencyViaObjectIntroduction(), SimpleLineSegments() } // Perform each .ForEach(configuration => { #region Finding theorems // Prepare 3 pictures in which the configuration is drawn var pictures = kernel.Get <IGeometryConstructor>().ConstructWithUniformLayout(configuration, numberOfPictures: 3).pictures; // Prepare a contextual picture var contextualPicture = new ContextualPicture(pictures); // Find all theorems var theorems = kernel.Get <ITheoremFinder>().FindAllTheorems(contextualPicture); #endregion #region Writing theorems // Prepare the formatter of all the output var formatter = new OutputFormatter(configuration.AllObjects); // Prepare a local function that converts given theorems to a string string TheoremString(IEnumerable <Theorem> theorems) => // If there are no theorems theorems.IsEmpty() // Then return an indication of it ? "nothing" // Otherwise format each theorem : theorems.Select(formatter.FormatTheorem) // Order alphabetically .Ordered() // Add the index .Select((theoremString, index) => $"[{index + 1}] {theoremString}") // Make each on a separate line .ToJoinedString("\n"); // Write the configuration and theorems Console.WriteLine($"\nConfiguration:\n\n{formatter.FormatConfiguration(configuration).Indent(2)}\n"); Console.WriteLine($"Theorems:\n\n{TheoremString(theorems.AllObjects).Indent(2)}\n"); #endregion #region Proving theorems // Prepare a timer var totalTime = new Stopwatch(); // Start it totalTime.Start(); // Perform the theorem finding with proofs, without any assumed theorems var proverOutput = kernel.Get <ITheoremProver>().ProveTheoremsAndConstructProofs(new TheoremMap(), theorems, contextualPicture); // Stop the timer totalTime.Stop(); #endregion #region Writing results // Get the proofs var proofString = proverOutput // Sort by the statement .OrderBy(pair => formatter.FormatTheorem(pair.Key)) // Format each .Select(pair => formatter.FormatTheoremProof(pair.Value)) // Trim .Select(proofString => proofString.Trim()) // Make an empty line between each .ToJoinedString("\n\n"); // Write it Console.WriteLine(proofString); // Write the unproven theorems too Console.WriteLine($"\nUnproved:\n\n{TheoremString(theorems.AllObjects.Except(proverOutput.Keys)).Indent(2)}\n"); // Report time Console.WriteLine($"Total time: {totalTime.ElapsedMilliseconds}"); Console.WriteLine("----------------------------------------------"); #endregion }); #endregion }
/// <inheritdoc/> public void Run(LoadedProblemGeneratorInput input) { #region Prepare readable writers // Prepare the name of readable output files var nameOfReadableFiles = $"{_settings.OutputFilePrefix}{input.Id}.{_settings.FileExtension}"; // If we should write readable output without proofs using var readableOutputWithoutProofsWriter = _settings.WriteReadableOutputWithoutProofs // Prepare the writer for it ? new StreamWriter(new FileStream(Path.Combine(_settings.ReadableOutputWithoutProofsFolder, nameOfReadableFiles), FileMode.Create, FileAccess.Write, FileShare.Read)) // Otherwise null : null; // If we should write readable output with proofs using var readableOutputWithProofsWriter = _settings.WriteReadableOutputWithProofs // Prepare the writer for it ? new StreamWriter(new FileStream(Path.Combine(_settings.ReadableOutputWithProofsFolder, nameOfReadableFiles), FileMode.Create, FileAccess.Write, FileShare.Read)) // Otherwise null : null; // Local function that writes a line to both readable writers, if they are available void WriteLineToBothReadableWriters(string line = "") { // Write to the standard writer readableOutputWithoutProofsWriter?.WriteLine(line); // Write to the writer with proofs readableOutputWithProofsWriter?.WriteLine(line); } #endregion #region Prepare JSON writer // Prepare the name of the JSON output file var jsonOutputFileName = $"{_settings.OutputFilePrefix}{input.Id}.json"; // If we should write the JSON output var jsonOutputWriter = _settings.WriteJsonOutput // Prepare the writer for it ? _factory.Create(Path.Combine(_settings.JsonOutputFolder, jsonOutputFileName)) // Otherwise null : null; #endregion // Call the generation algorithm var(initialTheorems, outputs) = _generator.Generate(input); #region Write constructions // Write the constructions header WriteLineToBothReadableWriters($"Constructions:\n"); // Write all of them input.Constructions.ForEach(construction => WriteLineToBothReadableWriters($" - {construction}")); // An empty line WriteLineToBothReadableWriters(); #endregion #region Write the initial configuration // Prepare the formatter for the initial configuration var initialFormatter = new OutputFormatter(input.InitialConfiguration.AllObjects); // Write it WriteLineToBothReadableWriters("Initial configuration:\n"); WriteLineToBothReadableWriters(initialFormatter.FormatConfiguration(input.InitialConfiguration)); // Write its theorems, if there are any if (initialTheorems.Any()) { WriteLineToBothReadableWriters("\nTheorems:\n"); WriteLineToBothReadableWriters(InitialTheoremsToString(initialFormatter, initialTheorems)); } #endregion #region Write other setup // Write iterations WriteLineToBothReadableWriters($"\nIterations: {input.NumberOfIterations}"); // Write maximal numbers of objects of particular types WriteLineToBothReadableWriters($"{input.MaximalNumbersOfObjectsToAdd.Select(pair => $"MaximalNumberOf{pair.Key}s: {pair.Value}").ToJoinedString("\n")}\n"); // Write whether we're excluding symmetry WriteLineToBothReadableWriters($"SymmetryGenerationMode: {input.SymmetryGenerationMode}"); #endregion // Write results header WriteLineToBothReadableWriters($"Results:"); // Log that we've started Log.Information("Generation has started."); #region Tracking variables // Prepare the number of generated configurations var numberOfGeneratedConfigurations = 0; // Prepare the total number of interesting theorems var numberOfInterestingTheorems = 0; // Prepare the total number of configurations with an interesting theorem var numberOfConfigurationsWithInterestingTheorem = 0; #endregion #region Start stopwatch // Prepare a stopwatch to measure the execution time var stopwatch = new Stopwatch(); // Start it stopwatch.Start(); #endregion // Begin writing of the JSON output file jsonOutputWriter?.BeginWriting(); // Prepare the variable indicating whether we're writing best theorems, // which happens when we want to write them either readable or JSON form var writeBestTheorems = _settings.WriteReadableBestTheorems || _settings.WriteJsonBestTheorems; // Prepare the variable indicating the last time we rewrote the best theorems DateTimeOffset?lastTimeBestTheoremsWereRewritten = null; #region Generation loop // Run the generation foreach (var generatorOutput in outputs) { // Mark the configuration numberOfGeneratedConfigurations++; #region Logging progress // Find out if we should log progress and if yes, do it if (_settings.LogProgress && numberOfGeneratedConfigurations % _settings.ProgressLoggingFrequency == 0) { // Calculate how long on average it takes to generate 'one batch' according to the progress frequency var averageTime = (double)stopwatch.ElapsedMilliseconds / numberOfGeneratedConfigurations * _settings.ProgressLoggingFrequency; // Based on whether we're creating best theorems, write how many of them we have var bestTheoremString = $"{(writeBestTheorems ? $" ({_resolver.AllSorters.Select(pair => pair.sorter.BestTheorems.Count()).Sum()} after global merge)" : "")}"; // Log what we have Log.Information("Generated configurations: {configurations}, after {time} ms, {frequency} in {averageTime:F2} ms on average, with {theorems} theorems{bestTheorems} " + "in {allConfigurations} configurations", numberOfGeneratedConfigurations, stopwatch.ElapsedMilliseconds, _settings.ProgressLoggingFrequency, averageTime, numberOfInterestingTheorems, bestTheoremString, numberOfConfigurationsWithInterestingTheorem); } #endregion // Skip configurations without theorems if (generatorOutput.NewTheorems.AllObjects.Count == 0) { continue; } // Prepare the output of the analyzer GeneratedProblemAnalyzerOutputBase analyzerOutput; #region Analyzer call try { // If we should look for proofs (because we should be writing them or analyze the inner inferences) analyzerOutput = _settings.WriteInferenceRuleUsages || _settings.WriteReadableOutputWithProofs // Then call the analysis that construct them ? (GeneratedProblemAnalyzerOutputBase)_analyzer.AnalyzeWithProofConstruction(generatorOutput, input.SymmetryGenerationMode) // Otherwise we don't need them : _analyzer.AnalyzeWithoutProofConstruction(generatorOutput, input.SymmetryGenerationMode); } catch (Exception e) { // If there is any sort of problem, we should make aware of it. Log.Error(e, "There has been an exception while analyzing the configuration:\n\n{configuration}\n", // Write the problematic configuration new OutputFormatter(generatorOutput.Configuration.AllObjects).FormatConfiguration(generatorOutput.Configuration)); // And move on, we still might get something cool continue; } #endregion // Count in interesting theorems numberOfInterestingTheorems += analyzerOutput.InterestingTheorems.Count; // If this is a configuration with an interesting theorem, count it in if (analyzerOutput.InterestingTheorems.Any()) { numberOfConfigurationsWithInterestingTheorem++; } // Write JSON output jsonOutputWriter?.Write(analyzerOutput.InterestingTheorems); #region Handling best theorems // If we are supposed to be handling best theorems, do so if (writeBestTheorems) { // Take the interesting theorems var theoremsToBeJudged = analyzerOutput.InterestingTheorems // Group by type .GroupBy(rankedTheorem => rankedTheorem.Theorem.Type); try { // Prepare the set of sorters whose content changed var updatedSorterTypes = new HashSet <TheoremType>(); // Mark all interesting theorems analyzerOutput.InterestingTheorems // Grouped by type .GroupBy(rankedTheorem => rankedTheorem.Theorem.Type) // Handle each group .ForEach(group => { // Let the sorter judge the theorems _resolver.GetSorterForType(group.Key).AddTheorems(group, out var localBestTheoremChanged); // If there is any local change, mark it if (localBestTheoremChanged) { updatedSorterTypes.Add(group.Key); } }); // Find out if we should rewrite the best theorems, i.e. it must be allowed var shouldWeRewriteBestTheorems = _settings.WriteBestTheoremsContinuously // And either we haven't done it yet && (lastTimeBestTheoremsWereRewritten == null || // Or the number of seconds that have passed since the last rewrote DateTimeOffset.Now.ToUnixTimeSeconds() - lastTimeBestTheoremsWereRewritten.Value.ToUnixTimeSeconds() // Is more than our specified interval > _settings.BestTheoremsRewrittingIntervalInSeconds); // If we should write best theorems continuously and it is time to it if (shouldWeRewriteBestTheorems) { // Do it RewriteBestTheorems(updatedSorterTypes); // After the update where it was done last time lastTimeBestTheoremsWereRewritten = DateTimeOffset.Now; } } catch (Exception e) { // If there is any sort of problem, we should make aware of it. Log.Error(e, "There has been an exception while sorting theorems of the configuration:\n\n{configuration}\n", // Write the problematic configuration new OutputFormatter(generatorOutput.Configuration.AllObjects).FormatConfiguration(generatorOutput.Configuration)); } } #endregion #region Human-readable output // Prepare a formatter for the generated configuration var formatter = new OutputFormatter(generatorOutput.Configuration.AllObjects); // Prepare the header so we can measure it var header = $"Configuration {numberOfGeneratedConfigurations}"; // Construct the header with dashes var headerWithConfiguration = $"\n{new string('-', header.Length)}\n{header}\n{new string('-', header.Length)}\n\n" + // And the configuration $"{formatter.FormatConfiguration(generatorOutput.Configuration)}"; #region Writing to the writer of readable output without proofs // If there is anything interesting to write if (analyzerOutput.InterestingTheorems.Any()) { // Write the header readableOutputWithoutProofsWriter?.Write(headerWithConfiguration); // Write the analysis results without proofs readableOutputWithoutProofsWriter?.Write(AnalyzerOutputToString(formatter, analyzerOutput, writeProofs: false)); // Flush it readableOutputWithProofsWriter?.Flush(); } #endregion #region Writing to the writer of output with proofs // Write the header readableOutputWithProofsWriter?.Write(headerWithConfiguration); // Write the analysis results with proofs readableOutputWithProofsWriter?.Write(AnalyzerOutputToString(formatter, analyzerOutput, writeProofs: true)); // Flush it readableOutputWithoutProofsWriter?.Flush(); #endregion #endregion #region Inference rule usage statistics // If we should write inference rule usages if (_settings.WriteInferenceRuleUsages) { // Mark the proofs _tracker.MarkProofs(((GeneratedProblemAnalyzerOutputWithProofs)analyzerOutput).TheoremProofs.Values); // Prepare the writer using var inferenceRuleUsageWriter = new StreamWriter(new FileStream(_settings.InferenceRuleUsageFilePath, FileMode.Create, FileAccess.Write, FileShare.Read)); // Rewrite the stats file inferenceRuleUsageWriter.Write(InferenceRuleUsagesToString()); } #endregion } #endregion // Rewrite the best theorems after the generation is finished RewriteBestTheorems(); // Prepare the string explaining the state after merge var afterMergeString = $"{(writeBestTheorems ? $"{_resolver.AllSorters.Select(pair => pair.sorter.BestTheorems.Count()).Sum()}" : "-")}"; // Write end WriteLineToBothReadableWriters("\n------------------------------------------------"); WriteLineToBothReadableWriters($"Generated configurations: {numberOfGeneratedConfigurations}"); WriteLineToBothReadableWriters($"Configurations with an interesting theorem: {numberOfConfigurationsWithInterestingTheorem}"); WriteLineToBothReadableWriters($"Interesting theorems: {numberOfInterestingTheorems}"); WriteLineToBothReadableWriters($"Interesting theorems after global merge: {afterMergeString}"); // Log these stats as well Log.Information("Generated configurations: {count}", numberOfGeneratedConfigurations); Log.Information("Configurations with an interesting theorem: {count}", numberOfConfigurationsWithInterestingTheorem); Log.Information("Interesting theorems: {count}", numberOfInterestingTheorems); Log.Information("Interesting theorems after global merge: {count}", afterMergeString); Log.Information("Run-time: {time} ms", stopwatch.ElapsedMilliseconds); // Close the JSON output writer jsonOutputWriter?.EndWriting(); }