Ejemplo n.º 1
0
        /// <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
        }