/// <summary> /// Proves given theorems that are true in the configuration drawn in a given picture. /// </summary> /// <param name="provenTheorems">The theorems that hold in the configuration without the last object.</param> /// <param name="theoremsToProve">The theorems that say something about the last object.</param> /// <param name="picture">The picture where the configuration in which the theorems hold is drawn.</param> /// <param name="shouldWeConstructProofs">Indicates whether we should construct proofs. This will affect the type of returned result.</param> /// <returns> /// Either the output as for <see cref="ProveTheoremsAndConstructProofs(TheoremMap, TheoremMap, ContextualPicture)"/>, /// if we are constructing proof, or the output as for <see cref="ProveTheorems(TheoremMap, TheoremMap, ContextualPicture)"/> otherwise. /// </returns> private dynamic ProveTheorems(TheoremMap provenTheorems, TheoremMap theoremsToProve, ContextualPicture picture, bool shouldWeConstructProofs) { // Pull the configuration for comfort var configuration = picture.Pictures.Configuration; // Find the trivial theorems based on whether we should do it only // for the last object of the configuration var trivialTheorems = _settings.FindTrivialTheoremsOnlyForLastObject // If yes, do so ? _producer.InferTrivialTheoremsFromObject(configuration.ConstructedObjects.Last()) // Otherwise do it for all objects : configuration.ConstructedObjects.SelectMany(_producer.InferTrivialTheoremsFromObject) // And enumerate the results .ToList(); #region Proof builder initialization // Prepare a proof builder in case we are supposed to construct proofs var proofBuilder = shouldWeConstructProofs ? new TheoremProofBuilder() : null; // Mark trivial theorems to the proof builder in case we are supposed to construct proofs trivialTheorems.ForEach(theorem => proofBuilder?.AddImplication(TrivialTheorem, theorem, assumptions: Array.Empty <Theorem>())); // Mark assumed theorems to the proof builder in case we are supposed to construct proofs provenTheorems.AllObjects.ForEach(theorem => proofBuilder?.AddImplication(AssumedProven, theorem, assumptions: Array.Empty <Theorem>())); #endregion #region Theorems definable simpler // Prepare the list of theorems definable simpler var theoremsDefinableSimpler = new List <Theorem>(); // If we are supposed to assuming them proven... if (_settings.AssumeThatSimplifiableTheoremsAreTrue) { // Go through unproven theorems except for trivial ones foreach (var theoremToProve in theoremsToProve.AllObjects.Except(trivialTheorems)) { // Find the redundant objects var redundantObjects = theoremToProve.GetUnnecessaryObjects(configuration); // If there are none, then it cannot be defined simpler if (redundantObjects.IsEmpty()) { continue; } // Otherwise add it to the list theoremsDefinableSimpler.Add(theoremToProve); // And make sure the proof builder knows it proofBuilder?.AddImplication(new DefinableSimplerInferenceData(redundantObjects), theoremToProve, assumptions: Array.Empty <Theorem>()); } } #endregion #region Theorems inferable from symmetry // Prepare the set of theorems inferred from symmetry var theoremsInferredFromSymmetry = new List <Theorem>(); // Go throw the theorems that are already inferred, i.e. assumed proven, trivial and definable simpler ones foreach (var provedTheorem in provenTheorems.AllObjects.Concat(trivialTheorems).Concat(theoremsDefinableSimpler)) { // Try to infer new theorems using this one foreach (var inferredTheorem in provedTheorem.InferTheoremsFromSymmetry(configuration)) { // Add it to the list theoremsInferredFromSymmetry.Add(inferredTheorem); // Make sure the proof builds knows it proofBuilder?.AddImplication(InferableFromSymmetry, inferredTheorem, assumptions: new[] { provedTheorem }); } } #endregion #region Normalization helper initialization // Initially we are going to assume that the proved theorems are the passed ones var provedTheorems = provenTheorems.AllObjects // And trivial ones .Concat(trivialTheorems) // And ones with redundant objects .Concat(theoremsDefinableSimpler) // And ones inferred from symmetry .Concat(theoremsInferredFromSymmetry) // Distinct ones .Distinct(); // The theorems to prove will be the new ones except for the proved ones var currentTheoremsToBeProven = theoremsToProve.AllObjects.Except(provedTheorems).ToArray(); // Prepare the cloned pictures that will be used to numerically verify new theorems var clonedPictures = picture.Pictures.Clone(); // Prepare a normalization helper with all this information var normalizationHelper = new NormalizationHelper(_verifier, clonedPictures, provedTheorems, currentTheoremsToBeProven); #endregion #region Scheduler initialization // Prepare a scheduler var scheduler = new Scheduler(_manager); // Do the initial scheduling scheduler.PerformInitialScheduling(currentTheoremsToBeProven, configuration); #endregion // Prepare the object introduction helper var objectIntroductionHelper = new ObjectIntroductionHelper(_introducer, normalizationHelper); #region Inference loop // Do until break while (true) { // Ask the scheduler for the next inference data to be used var data = scheduler.NextScheduledData(); #region Object introduction // If there is no data, we will try to introduce objects if (data == null) { // Call the introduction helper var(removedObjects, introducedObject) = objectIntroductionHelper.IntroduceObject( // With the theorems to prove obtained by excluding the proved ones theoremsToProve: theoremsToProve.AllObjects.Except(normalizationHelper.ProvedTheorems)); // Invalidate removed objects removedObjects.ForEach(scheduler.InvalidateObject); // If there is something to be introduced if (introducedObject != null) { // Call the appropriate method to handle introduced objects HandleNewObject(introducedObject, normalizationHelper, scheduler, proofBuilder); // Ask the scheduler for the next inference data to be used data = scheduler.NextScheduledData(); } } #endregion // If all theorems are proven or there is no data even after object introduction, we're done if (!normalizationHelper.AnythingLeftToProve || data == null) { // If we should construct proofs return(shouldWeConstructProofs // Build them for the theorems to be proven ? (dynamic)proofBuilder.BuildProofs(theoremsToProve.AllObjects) // Otherwise just take the theorems to be proven that happen to be proven : theoremsToProve.AllObjects.Where(normalizationHelper.ProvedTheorems.Contains).ToReadOnlyHashSet()); } #region Inference rule applier call // Try to apply the current scheduled data var applierResults = _applier.InferTheorems(new InferenceRuleApplierInput ( // Pass the data provided by the scheduler inferenceRule: data.InferenceRule, premappedAssumption: data.PremappedAssumption, premappedConclusion: data.PremappedConclusion, premappedObject: data.PremappedObject, // Pass the methods that the normalization helper offers mappableTheoremsFactory: normalizationHelper.GetProvedTheoremOfType, mappableObjectsFactory: normalizationHelper.GetObjectsWithConstruction, equalObjectsFactory: normalizationHelper.GetEqualObjects, normalizationFunction: normalizationHelper.GetNormalVersionOfObjectOrNull )) // Enumerate results. This step is needed because the applier could iterate over the // collections of objects and theorems used by the normalization helper .ToArray(); // Before handling results prepare a variable that will indicate whether there has been any change of the // normal version of an object. var anyNormalVersionChange = false; // Handle every inferred theorems foreach (var(theorem, negativeAssumptions, possitiveAssumptions) in applierResults) { // If in some previous iteration there has been a change of the normal version of an object, then // it might happen that some other theorems inferred in this loop no longer contain only correct objects, // therefore we need to verify them. The reason why we don't have to worry about incorrect objects in other // cases is that the normalization helper keeps everything normalized and the inference rule applier provides // only normalized objects. However, if there is a change of normal versions and the applier is already called // and the results are enumerated, then we have to check it manually if (anyNormalVersionChange && normalizationHelper.DoesTheoremContainAnyIncorrectObject(theorem)) { continue; } // We need to check negative assumptions. The inference should be accepted only if all of them are false if (negativeAssumptions.Any(negativeAssumption => _verifier.IsTrueInAllPictures(clonedPictures, negativeAssumption))) { continue; } // Prepare the proof data in case we need to construct proofs var proofData = shouldWeConstructProofs ? new ProofData(proofBuilder, new CustomInferenceData(data.InferenceRule), possitiveAssumptions) : null; // Prepare the variable indicating whether the theorem is geometrically valid bool isValid; // If this is an equality if (theorem.Type == EqualObjects) { // Call the appropriate method to handle it while finding out whether there has been any normal version change HandleEquality(theorem, normalizationHelper, scheduler, proofData, out isValid, out var anyNormalVersionChangeInThisIteration); // If yes, then we set the outer loop variable indicating the same thing for the whole loop if (anyNormalVersionChangeInThisIteration) { anyNormalVersionChange = true; } } // If this is a non-equality else { // Call the appropriate method to handle it HandleNonequality(theorem, normalizationHelper, scheduler, proofData, out isValid); } // If the theorem turns out not to be geometrically valid, trace it if (!isValid) { _tracer.MarkInvalidInferrence(configuration, theorem, data.InferenceRule, negativeAssumptions, possitiveAssumptions); } } #endregion } #endregion }