Repository: aimacode/aima-csharp Branch: master Commit: a8a4f15c4789 Files: 221 Total size: 584.1 KB Directory structure: gitextract_gi623cc4/ ├── .gitattributes ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── aima-csharp/ │ ├── App.config │ ├── Program.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── agent/ │ │ ├── Action.cs │ │ ├── Agent.cs │ │ ├── AgentProgram.cs │ │ ├── Environment.cs │ │ ├── EnvironmentObject.cs │ │ ├── EnvironmentState.cs │ │ ├── EnvironmentView.cs │ │ ├── EnvironmentViewNotifier.cs │ │ ├── Model.cs │ │ ├── Percept.cs │ │ ├── State.cs │ │ └── impl/ │ │ ├── AbstractAgent.cs │ │ ├── AbstractEnvironment.cs │ │ ├── DynamicAction.cs │ │ ├── DynamicEnvironmentState.cs │ │ ├── DynamicPercept.cs │ │ ├── DynamicState.cs │ │ ├── NoOpAction.cs │ │ ├── ObjectWithDynamicAttributes.cs │ │ ├── SimpleEnvironmentView.cs │ │ └── aprog/ │ │ ├── ModelBasedReflexAgentProgram.cs │ │ ├── SimpleReflexAgentProgram.cs │ │ ├── TableDrivenAgentProgram.cs │ │ └── simplerule/ │ │ ├── ANDCondition.cs │ │ ├── Condition.cs │ │ ├── EQUALCondition.cs │ │ ├── NOTCondition.cs │ │ ├── ORCondition.cs │ │ └── Rule.cs │ ├── aima-csharp.csproj │ ├── environment/ │ │ ├── cellworld/ │ │ │ ├── Cell.cs │ │ │ ├── CellWorld.cs │ │ │ ├── CellWorldAction.cs │ │ │ └── CellWorldFactory.cs │ │ ├── eightpuzzle/ │ │ │ ├── BidirectionalEightPuzzleProblem.cs │ │ │ ├── EightPuzzleBoard.cs │ │ │ ├── EightPuzzleFunctionFactory.cs │ │ │ ├── EightPuzzleGoalTest.cs │ │ │ ├── ManhattanHeuristicFunction.cs │ │ │ └── MisplacedTilleHeuristicFunction.cs │ │ ├── map/ │ │ │ ├── AdaptableHeuristicFunction.cs │ │ │ ├── BidirectionalMapProblem.cs │ │ │ ├── DynAttributeNames.cs │ │ │ ├── ExtendableMap.cs │ │ │ ├── Map.cs │ │ │ ├── MapAgent.cs │ │ │ ├── MapEnvironment.cs │ │ │ ├── MapEnvironmentState.cs │ │ │ ├── MapFunctionFactory.cs │ │ │ ├── MapStepCostFunction.cs │ │ │ ├── MoveToAction.cs │ │ │ ├── Scenario.cs │ │ │ ├── SimplifiedRoadMapOfAustralia.cs │ │ │ ├── SimplifiedRoadMapOfPartOfRomania.cs │ │ │ └── StraightLineDistanceHeuristicFunction.cs │ │ └── wumpusworld/ │ │ ├── AgentPercept.cs │ │ ├── AgentPosition.cs │ │ ├── ManhattanHeuristicFunction.cs │ │ └── Room.cs │ ├── logic/ │ │ ├── common/ │ │ │ ├── Lexer.cs │ │ │ ├── LexerException.cs │ │ │ ├── LogicTokenTypes.cs │ │ │ ├── Parser.cs │ │ │ ├── ParserException.cs │ │ │ ├── ParserTreeNode.cs │ │ │ └── Token.cs │ │ ├── fol/ │ │ │ ├── CNFConverter.cs │ │ │ ├── Connectors.cs │ │ │ ├── PredicateCollector.cs │ │ │ ├── Quantifiers.cs │ │ │ ├── StandardizeApart.cs │ │ │ ├── StandardizeApartInPlace.cs │ │ │ ├── StandardizeApartIndexical.cs │ │ │ ├── StandardizeApartIndexicalFactory.cs │ │ │ ├── StandardizeApartResult.cs │ │ │ ├── SubstVisitor.cs │ │ │ ├── SubsumptionElimination.cs │ │ │ ├── Unifier.cs │ │ │ ├── VariableCollector.cs │ │ │ ├── domain/ │ │ │ │ ├── DomainFactory.cs │ │ │ │ ├── FOLDomain.cs │ │ │ │ ├── FOLDomainAnswerLiteralAddedEvent.cs │ │ │ │ ├── FOLDomainEvent.cs │ │ │ │ ├── FOLDomainListener.cs │ │ │ │ ├── FOLDomainSkolemConstantAddedEvent.cs │ │ │ │ └── FOLDomainSkolemFunctionAddedEvent.cs │ │ │ ├── inference/ │ │ │ │ ├── AbstractModulation.cs │ │ │ │ ├── Demodulation.cs │ │ │ │ ├── FOLBCAsk.cs │ │ │ │ ├── FOLFCAsk.cs │ │ │ │ ├── FOLModelElimination.cs │ │ │ │ ├── FOLOTTERLikeTheoremProver.cs │ │ │ │ ├── FOLTFMResolution.cs │ │ │ │ ├── InferenceProcedure.cs │ │ │ │ ├── InferenceResult.cs │ │ │ │ ├── InferenceResultPrinter.cs │ │ │ │ ├── Paramodulation.cs │ │ │ │ ├── otter/ │ │ │ │ │ ├── ClauseFilter.cs │ │ │ │ │ ├── ClauseSimplifier.cs │ │ │ │ │ ├── LightestClauseHeuristic.cs │ │ │ │ │ └── defaultimpl/ │ │ │ │ │ ├── DefaultClauseFilter.cs │ │ │ │ │ ├── DefaultClauseSimplifier.cs │ │ │ │ │ └── DefaultLightestClauseHeuristic.cs │ │ │ │ ├── proof/ │ │ │ │ │ ├── AbstractProofStep.cs │ │ │ │ │ ├── Proof.cs │ │ │ │ │ ├── ProofFinal.cs │ │ │ │ │ ├── ProofPrinter.cs │ │ │ │ │ ├── ProofStep.cs │ │ │ │ │ ├── ProofStepBwChGoal.cs │ │ │ │ │ ├── ProofStepChainCancellation.cs │ │ │ │ │ ├── ProofStepChainContrapositive.cs │ │ │ │ │ ├── ProofStepChainDropped.cs │ │ │ │ │ ├── ProofStepChainFromClause.cs │ │ │ │ │ ├── ProofStepChainReduction.cs │ │ │ │ │ ├── ProofStepClauseBinaryResolvent.cs │ │ │ │ │ ├── ProofStepClauseClausifySentence.cs │ │ │ │ │ ├── ProofStepClauseDemodulation.cs │ │ │ │ │ ├── ProofStepClauseFactor.cs │ │ │ │ │ ├── ProofStepClauseParamodulation.cs │ │ │ │ │ ├── ProofStepFoChAlreadyAFact.cs │ │ │ │ │ ├── ProofStepFoChAssertFact.cs │ │ │ │ │ ├── ProofStepGoal.cs │ │ │ │ │ ├── ProofStepPremise.cs │ │ │ │ │ └── ProofStepRenaming.cs │ │ │ │ └── trace/ │ │ │ │ ├── FOLModelEliminationTracer.cs │ │ │ │ └── FOLTFMResolutiontracer.cs │ │ │ ├── kb/ │ │ │ │ ├── FOLKnowledgeBase.cs │ │ │ │ ├── FOLKnowledgeBaseFactory.cs │ │ │ │ └── data/ │ │ │ │ ├── CNF.cs │ │ │ │ ├── Chain.cs │ │ │ │ ├── Clause.cs │ │ │ │ ├── Literal.cs │ │ │ │ └── ReducedLiteral.cs │ │ │ └── parsing/ │ │ │ ├── AbstractFOLVisitor.cs │ │ │ ├── FOLLexer.cs │ │ │ ├── FOLParser.cs │ │ │ ├── FOLVisitor.cs │ │ │ └── ast/ │ │ │ ├── AtomicSentence.cs │ │ │ ├── ConnectedSentence.cs │ │ │ ├── Constant.cs │ │ │ ├── FOLNode.cs │ │ │ ├── Function.cs │ │ │ ├── NotSentence.cs │ │ │ ├── Predicate.cs │ │ │ ├── QuantifiedSentence.cs │ │ │ ├── Sentence.cs │ │ │ ├── Term.cs │ │ │ ├── TermEquality.cs │ │ │ └── Variable.cs │ │ └── propositional/ │ │ ├── agent/ │ │ │ └── KBAgent.cs │ │ ├── kb/ │ │ │ └── KnowledgeBase.cs │ │ └── parsing/ │ │ ├── PLVisitor.cs │ │ └── ast/ │ │ ├── AtomicSentence.cs │ │ ├── ComplexSentence.cs │ │ ├── Connective.cs │ │ ├── PropositionSymbol.cs │ │ └── Sentence.cs │ ├── search/ │ │ ├── Local/ │ │ │ ├── FitnessFunction.cs │ │ │ ├── Individual.cs │ │ │ └── Scheduler.cs │ │ ├── framework/ │ │ │ ├── CutOffIndicatorAction.cs │ │ │ ├── EvaluationFunction.cs │ │ │ ├── HeuristicFunction.cs │ │ │ ├── Metrics.cs │ │ │ ├── Node.cs │ │ │ ├── NodeExpander.cs │ │ │ ├── PathCostFunction.cs │ │ │ ├── PerceptToStateFunction.cs │ │ │ ├── Search.cs │ │ │ ├── SearchAgent.cs │ │ │ ├── SearchForActions.cs │ │ │ ├── SearchUtils.cs │ │ │ ├── SimpleProblemSolvingAgent.cs │ │ │ ├── SolutionChecker.cs │ │ │ ├── problem/ │ │ │ │ ├── ActionsFunction.cs │ │ │ │ ├── BidirectionalProblem.cs │ │ │ │ ├── DefaultGoalTest.cs │ │ │ │ ├── DefaultStepCostFunction.cs │ │ │ │ ├── GoalTest.cs │ │ │ │ ├── Problem.cs │ │ │ │ ├── ResultFunction.cs │ │ │ │ └── StepCostFunction.cs │ │ │ └── qsearch/ │ │ │ ├── GraphSearch.cs │ │ │ ├── GraphSearchBFS.cs │ │ │ ├── QueueSearch.cs │ │ │ └── TreeSearch.cs │ │ └── online/ │ │ ├── LRTAStarAgent.cs │ │ ├── OnlineDFSAgent.cs │ │ └── OnlineSearchProblem.cs │ └── util/ │ ├── ArrayIterator.cs │ ├── CSharpRandomizer.cs │ ├── DisjointSets.cs │ ├── FrequencyCounter.cs │ ├── Interval.cs │ ├── LUDecomposition.cs │ ├── LabeledGraph.cs │ ├── LinkedHashSet.cs │ ├── Matrix.cs │ ├── MixedRadixNumber.cs │ ├── MockRandomizer.cs │ ├── Pair.cs │ ├── Point2D.cs │ ├── Randomizer.cs │ ├── SetOps.cs │ ├── Table.cs │ ├── Triplet.cs │ ├── TwoKeyHashMap.cs │ ├── Util.cs │ ├── Vector.cs │ └── XYLocation.cs ├── aima-csharp-unity/ │ ├── .gitignore │ └── README.md └── aima-csharp.sln ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ [Xx]64/ [Xx]86/ [Bb]uild/ bld/ [Bb]in/ [Oo]bj/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Un-comment the next line if you do not want to checkin # your web deploy settings because they may include unencrypted # passwords #*.pubxml *.publishproj # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Microsoft Azure ApplicationInsights config file ApplicationInsights.config # Windows Store app package directory AppPackages/ BundleArtifacts/ # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ orleans.codegen.cs # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # LightSwitch generated files GeneratedArtifacts/ ModelManifest.xml # Paket dependency manager .paket/paket.exe # FAKE - F# Make .fake/ ================================================ FILE: CONTRIBUTING.md ================================================ How to Contribute to aima-csharp ========================== Thanks for considering contributing to `aima-csharp`! Whether you are an aspiring [Google Summer of Code](https://summerofcode.withgoogle.com/organizations/5663121491361792/) student, or an independent contributor, here is a guide to how you can help: ## Read the Code and Start on an Issue - First, read and understand the code to get a feel for the extent and the style. - Look at the [issues](https://github.com/aimacode/aima-csharp/issues) and pick one to work on. - One of the issues is that some algorithms are missing from the [list of algorithms](/README.md#index-of-implemented-algorithms). ## New and Improved Algorithms - Implement functions that were in the third edition of the book but were not yet implemented in the code. Check the [list of pseudocode algorithms (pdf)](https://github.com/aimacode/pseudocode/blob/master/aima3e-algorithms.pdf) to see what's missing. - As we finish chapters for the new fourth edition, we will share the new pseudocode in the [`aima-pseudocode`](https://github.com/aimacode/aima-pseudocode) repository, and describe what changes are necessary. We hope to have a `algorithm-name.md` file for each algorithm, eventually; it would be great if contributors could add some for the existing algorithms. Contributing a Patch ==================== 1. Submit an issue describing your proposed change to the repo in question (or work on an existing issue). 1. The repo owner will respond to your issue promptly. 1. Fork the desired repo, develop and test your code changes. 1. Submit a pull request. Reporting Issues ================ - Under which versions of Visual Studio does this happen? - Is anybody working on this? # Choice of Programming Languages Are we right to concentrate on Java and Python versions of the code? I think so; both languages are popular; Java is fast enough for our purposes, and has reasonable type declarations (but can be verbose); Python is popular and has a very direct mapping to the pseudocode in the book (but lacks type declarations and can be slow). The [TIOBE Index](http://www.tiobe.com/tiobe_index) says the top seven most popular languages, in order, are: Java, C, C++, C#, Python, PHP, Javascript So it might be reasonable to also support C++/C# at some point in the future. It might also be reasonable to support a language that combines the terse readability of Python with the type safety and speed of Java; perhaps Go or Julia. I see no reason to support PHP. Javascript is the language of the browser; it would be nice to have code that runs in the browser without need for any downloads; this would be in Javascript or a variant such as Typescript. There is also a `aima-lisp` project; in 1995 when we wrote the first edition of the book, Lisp was the right choice, but today it is less popular (currently #31 on the TIOBE index). ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 aimacode Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # ![](https://github.com/aimacode/aima-java/blob/gh-pages/aima3e/images/aima3e.jpg)aima-csharp C# implementation of algorithms from [Russell](http://www.cs.berkeley.edu/~russell/) And [Norvig's](http://www.norvig.com/) [Artificial Intelligence - A Modern Approach 3rd Edition](http://aima.cs.berkeley.edu/). You can use this in conjunction with a course on AI, or for study on your own. ## Index of Implemented Algorithms Here is a table of algorithms, the figure, name of the code in the book, and the file where they are implemented in the code. This chart was made for the third edition of the book and needs to be updated for the upcoming fourth edition. Empty implementations are a good place for contributors to look for an issue. The [aima-pseudocode](https://github.com/aimacode/aima-pseudocode) project describes all the algorithms from the book. |Fig|Page|Name (in book)|Code| | -------- |:--------:| :-----| :----- | |2|34|Environment|[Environment](/aima-csharp/agent/Environment.cs)| |2.1|35|Agent|[Agent](/aima-csharp/agent/Agent.cs)| |2.3|36|Table-Driven-Vacuum-Agent|| |2.7|47|Table-Driven-Agent|[TableDrivenAgentProgram](/aima-csharp/agent/impl/aprog/TableDrivenAgentProgram.cs)| |2.8|48|Reflex-Vacuum-Agent|[?ReflexVacuumAgent?](/aima-csharp/agent/impl/aprog/ModelBasedReflexAgentProgram.cs)| |2.10|49|Simple-Reflex-Agent|[SimpleReflexAgentProgram](/aima-csharp/agent/impl/aprog/SimpleReflexAgentProgram.cs)| |2.12|51|Model-Based-Reflex-Agent|[ModelBasedReflexAgentProgram](/aima-csharp/agent/impl/aprog/ModelBasedReflexAgentProgram.cs)| |3|66|Problem| |3.1|67|Simple-Problem-Solving-Agent|[SimpleProblemSolvingAgent](/aima-csharp/search/framework/SimpleProblemSolvingAgent.cs)| |3.2|68|Romania|[SimplifiedRoadMapOfPartOfRomania](/aima-csharp/environment/map/SimplifiedRoadMapOfPartOfRomania.cs)| |3.7|77|Tree-Search|[TreeSearch](/aima-csharp/search/framework/qsearch/TreeSearch.cs)| |3.7|77|Graph-Search|[GraphSearch](/aima-csharp/search/framework/qsearch/GraphSearch.cs)| |3.10|79|Node|[Node](/aima-csharp/search/framework/Node.cs)| |3.11|82|Breadth-First-Search| |3.14|84|Uniform-Cost-Search| |3|85|Depth-first Search| |3.17|88|Depth-Limited-Search| |3.18|89|Iterative-Deepening-Search| |3|90|Bidirectional search| |3|92|Best-First search| |3|92|Greedy best-First search| |3|93|A\* Search| |3.26|99|Recursive-Best-First-Search | |4.2|122|Hill-Climbing| |4.5|126|Simulated-Annealing| |4.8|129|Genetic-Algorithm| |4.11|136|And-Or-Graph-Search| |4|147|Online search problem|[OnlineSearchProblem](/aima-csharp/search/online/OnlineSearchProblem.cs)| |4.21|150|Online-DFS-Agent|[OnlineDFSAgent](/aima-csharp/search/online/OnlineDFSAgent.cs)| |4.24|152|LRTA\*-Agent|[LRTAStarAgent](/aima-csharp/search/online/LRTAStarAgent.cs)| |5.3|166|Minimax-Decision| |5.7|170|Alpha-Beta-Search| |6|202|CSP| |6.1|204|Map CSP| |6.3|209|AC-3| |6.5|215|Backtracking-Search| |6.8|221|Min-Conflicts| |6.11|224|Tree-CSP-Solver| |7|235|Knowledge Base| |7.1|236|KB-Agent|[KBAgent](/aima-csharp/logic/propositional/agent/KBAgent.cs)| |7.7|244|Propositional-Logic-Sentence|[Sentence](/aima-csharp/logic/propositional/parsing/ast/Sentence.cs)| |7.10|248|TT-Entails| |7|253|Convert-to-CNF| |7.12|255|PL-Resolution| |7.15|258|PL-FC-Entails?| |7.17|261|DPLL-Satisfiable?| |7.18|263|WalkSAT| |7.20|270|Hybrid-Wumpus-Agent| |7.22|272|SATPlan| |9|323|Subst| |9.1|328|Unify|[Unifier](/aima-csharp/logic/fol/Unifier.cs)| |9.3|332|FOL-FC-Ask|[FOLFCAsk](/aima-csharp/logic/fol/inference/FOLFCAsk.cs)| |9.3|332|FOL-BC-Ask|[FOLBCAsk](/aima-csharp/logic/fol/inference/FOLBCAsk.cs)| |9|345|CNF|[CNFConverter](/aima-csharp/logic/fol/CNFConverter.cs)| |9|347|Resolution|[FOLTFMResolution](/aima-csharp/logic/fol/inference/FOLTFMResolution.cs)| |9|354|Demodulation|| |9|354|Paramodulation|[Paramodulation](/aima-csharp/logic/fol/inference/Paramodulation.cs)| |9|345|Subsumption|[SubsumptionElimination](/aima-csharp/logic/fol/SubsumptionElimination.cs)| |10.9|383|Graphplan|---| |11.5|409|Hierarchical-Search|---| |11.8|414|Angelic-Search|---| |13.1|484|DT-Agent|---| |13|484|Probability-Model| |13|487|Probability-Distribution| |13|490|Full-Joint-Distribution| |14|510|Bayesian Network| |14.9|525|Enumeration-Ask| |14.11|528|Elimination-Ask| |14.13|531|Prior-Sample| |14.14|533|Rejection-Sampling| |14.15|534|Likelihood-Weighting| |14.16|537|GIBBS-Ask| |15.4|576|Forward-Backward| |15|578|Hidden Markov Model| |15.6|580|Fixed-Lag-Smoothing| |15|590|Dynamic Bayesian Network| |15.17|598|Particle-Filtering| |16.9|632|Information-Gathering-Agent|---| |17|647|Markov Decision Process| |17.4|653|Value-Iteration| |17.7|657|Policy-Iteration| |17.9|663|POMDP-Value-Iteration|---| |18.5|702|Decision-Tree-Learning| |18.8|710|Cross-Validation-Wrapper|---| |18.11|717|Decision-List-Learning| |18.24|734|Back-Prop-Learning| |18.34|751|AdaBoost| |19.2|771|Current-Best-Learning|---| |19.3|773|Version-Space-Learning|---| |19.8|786|Minimal-Consistent-Det|---| |19.12|793|FOIL|---| |21.2|834|Passive-ADP-Agent| |21.4|837|Passive-TD-Agent| |21.8|844|Q-Learning-Agent| |22.1|871|HITS| |23.5|894|CYK-Parse| |25.9|982|Monte-Carlo-Localization| # Index of data structures Here is a table of the implemented data structures, the figure, name of the implementation in the repository, and the file where they are implemented. | **Figure** | **Name (in repository)** | **File** | |:-----------|:-------------------------|:---------| | 3.2 | romania_map | | | 4.9 | vacumm_world | | | 4.23 | one_dim_state_space | | | 6.1 | australia_map | | | 7.13 | wumpus_world_inference | | | 7.16 | horn_clauses_KB | | | 17.1 | sequential_decision_environment | | | 18.2 | waiting_decision_tree | | ================================================ FILE: aima-csharp/App.config ================================================  ================================================ FILE: aima-csharp/Program.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace aima_csharp { class Program { static void Main(string[] args) { } } } ================================================ FILE: aima-csharp/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("aima-csharp")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("aima-csharp")] [assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("125f53d5-1ccf-4daf-82fe-4324686cf417")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: aima-csharp/agent/Action.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * Describes an Action that can or has been taken by an Agent via one of its Actuators. * * @author Ciaran O'Reilly */ public interface Action { /** * Indicates whether or not this Action is a 'No Operation'.
* Note: AIMA3e - NoOp, or no operation, is the name of an assembly language * instruction that does nothing. * * @return true if this is a NoOp Action. */ bool isNoOp(); } } ================================================ FILE: aima-csharp/agent/Agent.cs ================================================ using System; using System.Collections; namespace aima.core.agent { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 2.1, page 35.
* * Figure 2.1 Agents interact with environments through sensors and actuators. * * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface Agent : EnvironmentObject { /** * Call the Agent's program, which maps any given percept sequences to an * action. * * @param percept * The current percept of a sequence perceived by the Agent. * @return the Action to be taken in response to the currently perceived * percept. */ Action execute(Percept percept); /** * Life-cycle indicator as to the liveness of an Agent. * * @return true if the Agent is to be considered alive, false otherwise. */ bool isAlive(); /** * Set the current liveness of the Agent. * * @param alive * set to true if the Agent is to be considered alive, false * otherwise. */ void setAlive(bool alive); } } ================================================ FILE: aima-csharp/agent/AgentProgram.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * Artificial Intelligence A Modern Approach (3rd Edition): pg 35.
* An agent's behavior is described by the 'agent function' that maps any given * percept sequence to an action. Internally, the agent function for an * artificial agent will be implemented by an agent program. * * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface AgentProgram { /** * The Agent's program, which maps any given percept sequences to an action. * * @param percept * The current percept of a sequence perceived by the Agent. * @return the Action to be taken in response to the currently perceived * percept. */ Action execute(Percept percept); } } ================================================ FILE: aima-csharp/agent/Environment.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * An abstract description of possible discrete Environments in which Agent(s) * can perceive and act. * * @author Ravi Mohan * @author Ciaran O'Reilly * @author Mike Stampone */ public interface Environment { /** * Returns the Agents belonging to this Environment. * * @return The Agents belonging to this Environment. */ List getAgents(); /** * Add an agent to the Environment. * * @param agent * the agent to be added. */ void addAgent(Agent agent); /** * Remove an agent from the environment. * * @param agent * the agent to be removed. */ void removeAgent(Agent agent); /** * Returns the EnvironmentObjects that exist in this Environment. * * @return the EnvironmentObjects that exist in this Environment. */ List getEnvironmentObjects(); /** * Add an EnvironmentObject to the Environment. * * @param eo * the EnvironmentObject to be added. */ void addEnvironmentObject(EnvironmentObject eo); /** * Remove an EnvironmentObject from the Environment. * * @param eo * the EnvironmentObject to be removed. */ void removeEnvironmentObject(EnvironmentObject eo); /** * Move the Environment one time step forward. */ void step(); /** * Move the Environment n time steps forward. * * @param n * the number of time steps to move the Environment forward. */ void step(int n); /** * Step through time steps until the Environment has no more tasks. */ void stepUntilDone(); /** * Returns true if the Environment is finished with its current * task(s). * * @return true if the Environment is finished with its current * task(s). */ bool isDone(); /** * Retrieve the performance measure associated with an Agent. * * @param forAgent * the Agent for which a performance measure is to be retrieved. * @return the performance measure associated with the Agent. */ double getPerformanceMeasure(Agent forAgent); /** * Add a view on the Environment. * * @param ev * the EnvironmentView to be added. */ void addEnvironmentView(EnvironmentView ev); /** * Remove a view on the Environment. * * @param ev * the EnvironmentView to be removed. */ void removeEnvironmentView(EnvironmentView ev); /** * Notify all registered EnvironmentViews of a message. * * @param msg * the message to notify the registered EnvironmentViews with. */ void notifyViews(String msg); } } ================================================ FILE: aima-csharp/agent/EnvironmentObject.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * An interface used to indicate any object that can belong within an * Environment. * * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface EnvironmentObject { } } ================================================ FILE: aima-csharp/agent/EnvironmentState.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * An interface used to indicate a possible state of an Environment. * * @author Ciaran O'Reilly */ public interface EnvironmentState { } } ================================================ FILE: aima-csharp/agent/EnvironmentView.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * Allows external applications/logic to view the interaction of Agent(s) with * an Environment. * * @author Ravi Mohan * @author Ciaran O'Reilly * @author Mike Stampone */ public interface EnvironmentView { /** * A simple notification message from the Environment, from one of its * objects. * * @param msg * the message received. */ void notify(String msg); /** * Indicates an Agent has been added to the environment and what it * perceives initially. * * @param agent * the Agent just added to the Environment. * @param resultingState * the EnvironmentState that resulted from the Agent being added * to the Environment. */ void agentAdded(Agent agent, EnvironmentState resultingState); /** * Indicates the Environment has changed as a result of an Agent's action. * * @param agent * the Agent that performed the Action. * @param action * the Action the Agent performed. * @param resultingState * the EnvironmentState that resulted from the Agent's Action on * the Environment. */ void agentActed(Agent agent, Action action, EnvironmentState resultingState); } } ================================================ FILE: aima-csharp/agent/EnvironmentViewNotifier.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * @author Ciaran O'Reilly * */ public interface EnvironmentViewNotifier { /** * A simple notification message, to be forwarded to an Environment's * registered EnvironmentViews. * * @param msg * the message to be forwarded to the EnvironmentViews. */ void notifyViews(String msg); } } ================================================ FILE: aima-csharp/agent/Model.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * Artificial Intelligence A Modern Approach (3rd Edition): pg 50.
* * This knowledge about "how the world works" - whether implemented in simple bool circuits * or in complete scientific theories - is called a model of the world. An Agent that uses such a * model is called a model-based agent. * * @author Ciaran O'Reilly */ public interface Model { } } ================================================ FILE: aima-csharp/agent/Percept.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * Artificial Intelligence A Modern Approach (3rd Edition): pg 34.
* We use the term percept to refer the agent's perceptual inputs at any given instant. * * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface Percept { } } ================================================ FILE: aima-csharp/agent/State.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent { /** * Artificial Intelligence A Modern Approach (3rd Edition): pg 50.
* * The most effective way to handle partial observability is for the agent to keep track of the * part of the world it can't see now. That is, the agent should maintain some sort of internal * state that depends on the percept history and thereby reflects at least some of the unobserved * aspects of the current state. * * @author Ciaran O'Reilly */ public interface State { } } ================================================ FILE: aima-csharp/agent/impl/AbstractAgent.cs ================================================ using System; using aima.core.agent; namespace aima.core.agent.impl { /** * @author Ravi Mohan * @author Ciaran O'Reilly * @author Mike Stampone */ public abstract class AbstractAgent : Agent { protected AgentProgram program; private bool alive = true; public AbstractAgent() { } /** * Constructs an Agent with the specified AgentProgram. * * @param aProgram * the Agent's program, which maps any given percept sequences to * an action. */ public AbstractAgent(AgentProgram aProgram) { program = aProgram; } //START-Agent public virtual Action execute(Percept p) { if(null != program) { return program.execute(p); } return NoOpAction.NO_OP; } public bool isAlive() { return alive; } public void setAlive(bool alive) { this.alive = alive; } //END-Agent } } ================================================ FILE: aima-csharp/agent/impl/AbstractEnvironment.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; using aima.core.util; namespace aima.core.agent.impl { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public abstract class AbstractEnvironment : Environment, EnvironmentViewNotifier { // Note: Use LinkedHashSet's in order to ensure order is respected as // provide // access to these elements via List interface. protected LinkedHashSet envObjects = new LinkedHashSet(); protected LinkedHashSet agents = new LinkedHashSet(); protected LinkedHashSet views = new LinkedHashSet(); protected Dictionary performanceMeasures = new Dictionary(); // PRUBLIC METHODS // Methods to be implemented by subclasses. public abstract EnvironmentState getCurrentState(); public abstract EnvironmentState executeAction(Agent agent, Action action); public abstract Percept getPerceptSeenBy(Agent anAgent); /** * Method for implementing dynamic environments in which not all changes * are directly caused by agent action execution. The default implementation * does nothing. */ public void createExogenousChange() { } // START-Environment public List getAgents() { // Return as a List but also ensures the caller cannot modify return new List(agents); } public void addAgent(Agent a) { addEnvironmentObject(a); } public void removeAgent(Agent a) { removeEnvironmentObject(a); } public List getEnvironmentObjects() { // Return as a List but also ensures the caller cannot modify return new List(envObjects); } public void addEnvironmentObject(EnvironmentObject eo) { envObjects.Add(eo); if (eo is Agent) { Agent a = (Agent)eo; if (!agents.Contains(a)) { agents.Add(a); this.updateEnvironmentViewsAgentAdded(a); } } } public void removeEnvironmentObject(EnvironmentObject eo) { envObjects.Remove(eo); //agents.Remove(eo); } /** * Central template method for controlling agent simulation. The * concrete behavior is determined by the primitive operations * {@link #getPerceptSeenBy(Agent)}, {@link #executeAction(Agent, Action)}, * and {@link #createExogenousChange()}. */ public void step() { foreach (Agent agent in agents) { if (agent.isAlive()) { Action anAction = agent.execute(getPerceptSeenBy(agent)); EnvironmentState es = executeAction(agent, anAction); updateEnvironmentViewsAgentActed(agent, anAction, es); } } createExogenousChange(); } public void step(int n) { for (int i = 0; i < n; i++) { step(); } } public void stepUntilDone() { while (!isDone()) { step(); } } public bool isDone() { foreach (Agent agent in agents) { if (agent.isAlive()) { return false; } } return true; } public double getPerformanceMeasure(Agent forAgent) { Double pm = performanceMeasures[forAgent]; if (null == pm) { pm = 0.0; performanceMeasures[forAgent] = pm; } return pm; } public void addEnvironmentView(EnvironmentView ev) { views.Add(ev); } public void removeEnvironmentView(EnvironmentView ev) { views.Remove(ev); } public void notifyViews(String msg) { foreach (EnvironmentView ev in views) { ev.notify(msg); } } // END-Environment // PROTECTED METHODS protected void updatePerformanceMeasure(Agent forAgent, double addTo) { performanceMeasures[forAgent] = getPerformanceMeasure(forAgent) + addTo; } protected void updateEnvironmentViewsAgentAdded(Agent agent) { foreach (EnvironmentView view in views) { view.agentAdded(agent, getCurrentState()); } } protected void updateEnvironmentViewsAgentActed(Agent agent, Action action, EnvironmentState state) { foreach (EnvironmentView view in views) { view.agentActed(agent, action, state); } } } } ================================================ FILE: aima-csharp/agent/impl/DynamicAction.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; namespace aima.core.agent.impl { /** * @author Ciaran O'Reilly */ public class DynamicAction : ObjectWithDynamicAttributes, Action { public const String ATTRIBUTE_NAME = "name"; public DynamicAction(String name) { this.setAttribute(ATTRIBUTE_NAME, name); } /** * Returns the value of the name attribute. * * @return the value of the name attribute. */ public String getName() { return (System.String)getAttribute(ATTRIBUTE_NAME); } // START-Action public bool isNoOp() { return false; } // END-Action public String describeType() { return this.GetType().Name; } } } ================================================ FILE: aima-csharp/agent/impl/DynamicEnvironmentState.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; namespace aima.core.agent.impl { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class DynamicEnvironmentState : ObjectWithDynamicAttributes, EnvironmentState { public DynamicEnvironmentState() { } public String describeType() { return typeof(EnvironmentState).Name; } } } ================================================ FILE: aima-csharp/agent/impl/DynamicPercept.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; using System.Diagnostics; namespace aima.core.agent.impl { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class DynamicPercept : ObjectWithDynamicAttributes, Percept { public DynamicPercept() { } public String describeType() { return typeof(Percept).Name; } /** * Constructs a DynamicPercept with one attribute * * @param key1 * the attribute key * @param value1 * the attribute value */ public DynamicPercept(Object key1, Object value1) { setAttribute(key1, value1); } /** * Constructs a DynamicPercept with two attributes * * @param key1 * the first attribute key * @param value1 * the first attribute value * @param key2 * the second attribute key * @param value2 * the second attribute value */ public DynamicPercept(Object key1, Object value1, Object key2, Object value2) { setAttribute(key1, value1); setAttribute(key2, value2); } /** * Constructs a DynamicPercept with an array of attributes * * @param keys * the array of attribute keys * @param values * the array of attribute values */ public DynamicPercept(Object[] keys, Object[] values) { Debug.Assert(keys.Length == values.Length); for(int i = 0; i < keys.Length; i++) { setAttribute(keys[i], values[i]); } } } } ================================================ FILE: aima-csharp/agent/impl/DynamicState.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; namespace aima.core.agent.impl { /** * @author Ciaran O'Reilly */ public class DynamicState : ObjectWithDynamicAttributes, State { public DynamicState() { } public String describeType() { return typeof(State).Name; } } } ================================================ FILE: aima-csharp/agent/impl/NoOpAction.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.agent.impl { public class NoOpAction : DynamicAction { public static readonly NoOpAction NO_OP = new NoOpAction(); // START-Action public bool isNoOp() { return true; } //END-Action private NoOpAction() : base("NoOp") { } } } ================================================ FILE: aima-csharp/agent/impl/ObjectWithDynamicAttributes.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; namespace aima.core.agent.impl { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public abstract class ObjectWithDynamicAttributes { private Dictionary attributes = new Dictionary(); //PUBLIC METHODS /** * By default, returns the simple name of the underlying class as given in * the source code. * * @return the simple name of the underlying class */ public String describeType() { return this.GetType().Name; } /** * Returns a string representation of the object's current attributes * * @return a string representation of the object's current attributes */ public String describeAttributes() { StringBuilder sb = new StringBuilder(); sb.Append("["); bool first = true; foreach (Object key in attributes.Keys) { if (first) { first = false; } else { sb.Append(", "); } sb.Append(key); sb.Append("=="); sb.Append(attributes[key]); } sb.Append("]"); return sb.ToString(); } /** * Returns an unmodifiable view of the object's key set * * @return an unmodifiable view of the object's key set */ public HashSet getKeySet() { return new HashSet(attributes.Keys); } /** * Associates the specified value with the specified attribute key. If the * ObjectWithDynamicAttributes previously contained a mapping for the * attribute key, the old value is replaced. * * @param key * the attribute key * @param value * the attribute value */ public void setAttribute(Object key, Object value) { attributes[key] = value; } /** * Returns the value of the specified attribute key, or null if the * attribute was not found. * * @param key * the attribute key * * @return the value of the specified attribute name, or null if not found. */ public Object getAttribute(Object key) { return attributes[key]; } /** * Removes the attribute with the specified key from this * ObjectWithDynamicAttributes. * * @param key * the attribute key */ public void removeAttribute(Object key) { attributes.Remove(key); } /** * Creates and returns a copy of this ObjectWithDynamicAttributes */ public ObjectWithDynamicAttributes copy() { ObjectWithDynamicAttributes copy = null; try { copy = (ObjectWithDynamicAttributes)this.GetType().GetConstructor(System.Type.EmptyTypes).Invoke(null); foreach (object val in attributes) { copy.attributes.Add(val, attributes[val]); } } catch (Exception ex) { Debug.WriteLine(ex.ToString()); } return copy; } public override bool Equals(Object o) { if (o == null || this.GetType() != o.GetType()) { return base.Equals(o); } return attributes.Equals(((ObjectWithDynamicAttributes)o).attributes); } public override int GetHashCode() { return attributes.GetHashCode(); } public override String ToString() { StringBuilder sb = new StringBuilder(); sb.Append(describeType()); sb.Append(describeAttributes()); return sb.ToString(); } } } ================================================ FILE: aima-csharp/agent/impl/SimpleEnvironmentView.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; namespace aima.core.agent.impl { /** * Simple environment view which uses the standard output stream to inform about * relevant events. * * @author Ruediger Lunde */ public class SimpleEnvironmentView : EnvironmentView { public void agentActed(Agent agent, Action action, EnvironmentState resultingState) { System.Console.WriteLine("Agent acted: " + action.ToString()); } public void agentAdded(Agent agent, EnvironmentState resultingState) { System.Console.WriteLine("Agent added."); } public void notify(string msg) { System.Console.WriteLine("Message: " + msg); } } } ================================================ FILE: aima-csharp/agent/impl/aprog/ModelBasedReflexAgentProgram.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl.aprog.simplerule; namespace aima.core.agent.impl.aprog { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 2.12, page * 51.
*
* *
 * function MODEL-BASED-REFLEX-AGENT(percept) returns an action
 *   persistent: state, the agent's current conception of the world state
 *               model, a description of how the next state depends on current state and action
 *               rules, a set of condition-action rules
 *               action, the most recent action, initially none
 *               
 *   state  <- UPDATE-STATE(state, action, percept, model)
 *   rule   <- RULE-MATCH(state, rules)
 *   action <- rule.ACTION
 *   return action
 * 
* * Figure 2.12 A model-based reflex agent. It keeps track of the current state * of the world using an internal model. It then chooses an action in the same * way as the reflex agent. * * @author Ciaran O'Reilly * @author Mike Stampone * */ public abstract class ModelBasedReflexAgentProgram : AgentProgram { // persistent: state, the agent's current conception of the world state private DynamicState state = null; // model, a description of how the next state depends on current state and // action private Model model = null; // rules, a set of condition-action rules private HashSet rules = null; // action, the most recent action, initially none private Action action = null; public ModelBasedReflexAgentProgram() { init(); } /** * Set the agent's current conception of the world state. * * @param state * the agent's current conception of the world state. */ public void setState(DynamicState dstate) { state = dstate; } /** * Set the program's description of how the next state depends on the state * and action. * * @param model * a description of how the next state depends on the current * state and action. */ public void setModel(Model mod) { model = mod; } /** * Set the program's condition-action rules * * @param ruleSet * a set of condition-action rules */ public void setRules(HashSet ruleSet) { rules = ruleSet; } //START-AgentProgram // function MODEL-BASED-REFLEX-AGENT(percept) returns an action public Action execute(Percept percept) { // state <- UPDATE-STATE(state, action, percept, model) state = updateState(state, action, percept, model); // rule <- RULE-MATCH(state, rules) Rule rule = ruleMatch(state, rules); // action <- rule.ACTION action = ruleAction(rule); // return action return action; } // END-AgentProgram // PROTECTED METHODS /** * Realizations of this class should implement the init() method so that it * calls the setState(), setModel(), and setRules() method. */ protected abstract void init(); protected abstract DynamicState updateState(DynamicState state, Action action, Percept percept, Model model); protected Rule ruleMatch(DynamicState state, HashSet rules) { foreach (Rule r in rules) { if (r.evaluate(state)) { return r; } } return null; } protected Action ruleAction(Rule r) { return null == r ? NoOpAction.NO_OP : r.getAction(); } } } ================================================ FILE: aima-csharp/agent/impl/aprog/SimpleReflexAgentProgram.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.agent.impl.aprog.simplerule; namespace aima.core.agent.impl.aprog { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 2.10, page * 49.
*
* *
     * function SIMPLE-RELEX-AGENT(percept) returns an action
     *   persistent: rules, a set of condition-action rules
     *        
     * state  <- INTERPRET-INPUT(percept);
     * rule   <- RULE-MATCH(state, rules);
     * action <- rule.ACTION;
     * return action
     * 
* Figure 2.10 A simple reflex agent. It acts according to a rule whose * condition matches the current state, as defined by the percept. * * @author Ciaran O'Reilly * @author Mike Stampone * */ public class SimpleReflexAgentProgram : AgentProgram { // persistent: rules, a set of condition-action rules private HashSet rules; /** * Constructs a SimpleReflexAgentProgram with a set of condition-action * rules. * * @param ruleSet * a set of condition-action rules */ public SimpleReflexAgentProgram(HashSet ruleSet) { rules = ruleSet; } // START-AgentProgram // function SIMPLE-RELEX-AGENT(percept) returns an action public Action execute(Percept percept) { // state <- INTERPRET-INPUT(percept); ObjectWithDynamicAttributes state = interpretInput(percept); // rule <- RULE-MATCH(state, rules); Rule rule = ruleMatch(state, rules); // action <- rule.ACTION; // return action return ruleAction(rule); } // END-AgentProgram // PROTECTED METHODS protected ObjectWithDynamicAttributes interpretInput(Percept p) { return (DynamicPercept)p; } protected Rule ruleMatch(ObjectWithDynamicAttributes state, HashSet rulesSet) { foreach (Rule r in rulesSet) { if (r.evaluate(state)) { return r; } } return null; } protected Action ruleAction(Rule r) { return null == r ? NoOpAction.NO_OP : r.getAction(); } } } ================================================ FILE: aima-csharp/agent/impl/aprog/TableDrivenAgentProgram.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.util; namespace aima.core.agent.impl.aprog { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 2.7, page 47.
*
* *
     * function TABLE-DRIVEN-AGENT(percept) returns an action
     *   persistent: percepts, a sequence, initially empty
     *               table, a table of actions, indexed by percept sequences, initially fully specified
     *           
     *   append percept to end of percepts
     *   action <- LOOKUP(percepts, table)
     *   return action
     * 
* * Figure 2.7 The TABLE-DRIVEN-AGENT program is invoked for each new percept and * returns an action each time. It retains the complete percept sequence in * memory. * * @author Ciaran O'Reilly * @author Mike Stampone * */ public class TableDrivenAgentProgram : AgentProgram { private List percepts = new List(); private Table, System.String, Action> table; private const System.String ACTION = "action"; // persistent: percepts, a sequence, initially empty // table, a table of actions, indexed by percept sequences, initially fully // specified /** * Constructs a TableDrivenAgentProgram with a table of actions, indexed by * percept sequences. * * @param perceptSequenceActions * a table of actions, indexed by percept sequences */ public TableDrivenAgentProgram(Dictionary, Action> perceptSequenceActions) { List> rowHeaders = new List>(perceptSequenceActions.Keys); List colHeaders = new List(); colHeaders.Add(ACTION); table = new Table, System.String, Action>(rowHeaders, colHeaders); foreach (List row in rowHeaders) { table.set(row, ACTION, perceptSequenceActions[row]); } } // START-AgentProgram // function TABLE-DRIVEN-AGENT(percept) returns an action public Action execute(Percept percept) { // append percept to end of percepts percepts.Add(percept); // action <- LOOKUP(percepts, table) // return action return lookupCurrentAction(); } //END-AgentProgram //PRIVATE METHODS private Action lookupCurrentAction() { Action action = null; action = table.get(percepts, ACTION); if (null == action) { action = NoOpAction.NO_OP; } return action; } } } ================================================ FILE: aima-csharp/agent/impl/aprog/simplerule/ANDCondition.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using aima.core.agent.impl; namespace aima.core.agent.impl.aprog.simplerule { /** * Implementation of an AND condition. * * @author Ciaran O'Reilly * */ public class ANDCondition : Condition { private Condition left; private Condition right; public ANDCondition(Condition leftCon, Condition rightCon) { Debug.Assert(null != leftCon); Debug.Assert(null != rightCon); left = leftCon; right = rightCon; } public override bool evaluate(ObjectWithDynamicAttributes p) { return (left.evaluate(p) && right.evaluate(p)); } public override String ToString() { StringBuilder sb = new StringBuilder(); return sb.Append("[").Append(left).Append(" && ").Append(right).Append( "]").ToString(); } } } ================================================ FILE: aima-csharp/agent/impl/aprog/simplerule/Condition.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent.impl; namespace aima.core.agent.impl.aprog.simplerule { /** * Base abstract class for describing conditions. * * @author Ciaran O'Reilly * */ public abstract class Condition { public abstract bool evaluate(ObjectWithDynamicAttributes p); public bool Equals(Object o) { if(o == null || !(o is Condition)) { return base.Equals(o); } return (ToString().Equals(((Condition)o).ToString())); } public override int GetHashCode() { return ToString().GetHashCode(); } } } ================================================ FILE: aima-csharp/agent/impl/aprog/simplerule/EQUALCondition.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using aima.core.agent.impl; namespace aima.core.agent.impl.aprog.simplerule { /** * Implementation of an EQUALity condition. * * @author Ciaran O'Reilly * */ public class EQUALCondition : Condition { private Object key; private Object value; public EQUALCondition(Object k, Object val) { Debug.Assert(null != key); Debug.Assert(null != value); key = k; value = val; } public override bool evaluate(ObjectWithDynamicAttributes p) { return value.Equals(p.getAttribute(key)); } public override String ToString() { StringBuilder sb = new StringBuilder(); return sb.Append(key).Append("==").Append(value).ToString(); } } } ================================================ FILE: aima-csharp/agent/impl/aprog/simplerule/NOTCondition.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using aima.core.agent.impl; namespace aima.core.agent.impl.aprog.simplerule { /** * Implementation of a NOT condition. * * @author Ciaran O'Reilly * */ public class NOTCondition : Condition { private Condition con; public NOTCondition(Condition c) { Debug.Assert(null != con); con = c; } public override bool evaluate(ObjectWithDynamicAttributes p) { return (!con.evaluate(p)); } public override String ToString() { StringBuilder sb = new StringBuilder(); return sb.Append("![").Append(con).Append("]").ToString(); } } } ================================================ FILE: aima-csharp/agent/impl/aprog/simplerule/ORCondition.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using aima.core.agent.impl; namespace aima.core.agent.impl.aprog.simplerule { /** * Implementation of an OR condition. * * @author Ciaran O'Reilly * */ public class ORCondition : Condition { private Condition left; private Condition right; public ORCondition(Condition leftCon, Condition rightCon) { Debug.Assert(null != leftCon); Debug.Assert(null != rightCon); left = leftCon; right = rightCon; } public override bool evaluate(ObjectWithDynamicAttributes p) { return (left.evaluate(p) || right.evaluate(p)); } public override String ToString() { StringBuilder sb = new StringBuilder(); return sb.Append("[").Append(left).Append(" || ").Append(right).Append( "]").ToString(); } } } ================================================ FILE: aima-csharp/agent/impl/aprog/simplerule/Rule.cs ================================================ using System.Collections.Generic; using System.Text; using System.Diagnostics; using aima.core.agent; using aima.core.agent.impl; namespace aima.core.agent.impl.aprog.simplerule { /** * A simple implementation of a "condition-action rule". * * @author Ciaran O'Reilly * @author Mike Stampone */ public class Rule { private Condition con; private Action action; /** * Constructs a condition-action rule. * * @param con * a condition * @param action * an action */ public Rule(Condition c, Action act) { Debug.Assert(null != con); Debug.Assert(null != action); con = c; action = act; } public bool evaluate(ObjectWithDynamicAttributes p) { return (con.evaluate(p)); } /** * Returns the action of this condition-action rule. * * @return the action of this condition-action rule. */ public Action getAction() { return action; } public override bool Equals(System.Object o) { if (o == null || !(o is Rule)) { return base.Equals(o); } return (ToString().Equals(((Rule)o).ToString())); } public override int GetHashCode() { return ToString().GetHashCode(); } public override System.String ToString() { StringBuilder sb = new StringBuilder(); return sb.Append("if ").Append(con).Append(" then ").Append(action) .Append(".").ToString(); } } } ================================================ FILE: aima-csharp/aima-csharp.csproj ================================================  Debug AnyCPU {125F53D5-1CCF-4DAF-82FE-4324686CF417} Exe Properties aima_csharp aima-csharp v4.5.2 512 true AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 AnyCPU pdbonly true bin\Release\ TRACE prompt 4 ================================================ FILE: aima-csharp/environment/cellworld/Cell.cs ================================================ using System; namespace aima.core.environment.cellworld { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 645.
*
* A representation of a Cell in the environment detailed in Figure 17.1. * * @param * the content type of the cell. * * @author Ciaran O'Reilly * @author Ravi Mohan */ public class Cell { private int x = 1; private int y = 1; private C content = default(C); /** * Construct a Cell. * * @param x * the x position of the cell. * @param y * the y position of the cell. * @param content * the initial content of the cell. */ public Cell(int x, int y, C content) { this.x = x; this.y = y; this.content = content; } /** * * @return the x position of the cell. */ public int getX() { return x; } /** * * @return the y position of the cell. */ public int getY() { return y; } /** * * @return the content of the cell. */ public C getContent() { return content; } /** * Set the cell's content. * * @param content * the content to be placed in the cell. */ public void setContent(C content) { this.content = content; } public String toString() { return ""; } public bool equals(Object o) { if (o is Cell) { Cell c = (Cell ) o; return x == c.x && y == c.y && content.Equals(c.content); } return false; } public int hashCode() { return x + 23 + y + 31 * content.GetHashCode(); } } } ================================================ FILE: aima-csharp/environment/cellworld/CellWorld.cs ================================================ using System; using System.Collections.Generic; using aima.core.util; namespace aima.core.environment.cellworld { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 645.
*
* * A representation for the environment depicted in figure 17.1.
*
* Note: the x and y coordinates are always positive integers starting at * 1.
* Note: If looking at a rectangle - the coordinate (x=1, y=1) will be the * bottom left hand corner.
* * * @param * the type of content for the Cells in the world. * * @author Ciaran O'Reilly * @author Ravi Mohan */ public class CellWorld { private LinkedHashSet> cells = new LinkedHashSet>(); private Dictionary>> cellLookup = new Dictionary>>(); /** * Construct a Cell World with size xDimension * y Dimension cells, all with * their values set to a default content value. * * @param xDimension * the size of the x dimension. * @param yDimension * the size of the y dimension. * * @param defaultCellContent * the default content to assign to each cell created. */ public CellWorld(int xDimension, int yDimension, C defaultCellContent) { for (int x = 1; x <= xDimension; x++) { Dictionary> xCol = new Dictionary>(); for (int y = 1; y <= yDimension; y++) { Cell c = new Cell(x, y, defaultCellContent); cells.Add(c); xCol[y] = c; } cellLookup[x] = xCol; } } /** * * @return all the cells in this world. */ public LinkedHashSet> getCells() { return cells; } /** * Determine what cell would be moved into if the specified action is * performed in the specified cell. Normally, this will be the cell adjacent * in the appropriate direction. However, if there is no cell in the * adjacent direction of the action then the outcome of the action is to * stay in the same cell as the action was performed in. * * @param s * the cell location from which the action is to be performed. * @param a * the action to perform (Up, Down, Left, or Right). * @return the Cell an agent would end up in if they performed the specified * action from the specified cell location. */ public Cell result(Cell s, CellWorldAction a) { Cell sDelta = getCellAt(a.getXResult(s.getX()), a.getYResult(s .getY())); if (null == sDelta) { // Default to no effect // (i.e. bumps back in place as no adjoining cell). sDelta = s; } return sDelta; } /** * Remove the cell at the specified location from this Cell World. This * allows you to introduce barriers into different location. * * @param x * the x dimension of the cell to be removed. * @param y * the y dimension of the cell to be removed. */ public void removeCell(int x, int y) { Dictionary> xCol = cellLookup[x]; if (null != xCol) { xCol.Remove(y); cells.Remove(xCol[y]); } } /** * Get the cell at the specified x and y locations. * * @param x * the x dimension of the cell to be retrieved. * @param y * the y dimension of the cell to be retrieved. * @return the cell at the specified x,y location, null if no cell exists at * this location. */ public Cell getCellAt(int x, int y) { Cell c = null; Dictionary> xCol = cellLookup[x]; if (null != xCol) { c = xCol[y]; } return c; } } } ================================================ FILE: aima-csharp/environment/cellworld/CellWorldAction.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.util; namespace aima.core.environment.cellworld { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 645.
*
* * The actions in every state are Up, Down, Left, and Right.
*
* Note: Moving 'North' causes y to increase by 1, 'Down' y to decrease by * 1, 'Left' x to decrease by 1, and 'Right' x to increase by 1 within a Cell * World. * * @author Ciaran O'Reilly * */ public class CellWorldAction : Action { public CellWorldAction() { } public CellWorldAction(ActionEnum action) { this._action = action; } public enum ActionEnum { Up, Down, Left, Right, None }; private ActionEnum _action; private static readonly LinkedHashSet _actions = new LinkedHashSet(); static CellWorldAction() { _actions.Add(ActionEnum.Up); _actions.Add(ActionEnum.Down); _actions.Add(ActionEnum.Left); _actions.Add(ActionEnum.Right); _actions.Add(ActionEnum.None); } /** * * @return a set of the actual actions. */ public static LinkedHashSet actions() { return _actions; } // START-Action public bool isNoOp() { if (this._action == ActionEnum.None) { return true; } return false; } // END-Action /** * * @param curX * the current x position. * @return the result on the x position of applying this action. */ public int getXResult(int curX) { int newX = curX; switch (this._action) { case ActionEnum.Left: newX--; break; case ActionEnum.Right: newX++; break; } return newX; } /** * * @param curY * the current y position. * @return the result on the y position of applying this action. */ public int getYResult(int curY) { int newY = curY; switch (this._action) { case ActionEnum.Up: newY++; break; case ActionEnum.Down: newY--; break; } return newY; } /** * * @return the first right angled action related to this action. */ public ActionEnum getFirstRightAngledAction() { ActionEnum a = ActionEnum.None; switch (this._action) { case ActionEnum.Up: case ActionEnum.Down: a = ActionEnum.Left; break; case ActionEnum.Left: case ActionEnum.Right: a = ActionEnum.Down; break; case ActionEnum.None: a = ActionEnum.None; break; } return a; } /** * * @return the second right angled action related to this action. */ public ActionEnum getSecondRightAngledAction() { ActionEnum a = ActionEnum.None; switch (this._action) { case ActionEnum.Up: case ActionEnum.Down: a = ActionEnum.Right; break; case ActionEnum.Left: case ActionEnum.Right: a = ActionEnum.Up; break; case ActionEnum.None: a = ActionEnum.None; break; } return a; } } } ================================================ FILE: aima-csharp/environment/cellworld/CellWorldFactory.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.environment.cellworld { /** * * @author Ciaran O'Reilly * */ public class CellWorldFactory { /** * Create the cell world as defined in Figure 17.1 in AIMA3e. (a) A simple 4 * x 3 environment that presents the agent with a sequential decision * problem. * * @return a cell world representation of Fig 17.1 in AIMA3e. */ public static CellWorld createCellWorldForFig17_1() { CellWorld cw = new CellWorld(4, 3, -0.04); cw.removeCell(2, 2); cw.getCellAt(4, 3).setContent(1.0); cw.getCellAt(4, 2).setContent(-1.0); return cw; } } } ================================================ FILE: aima-csharp/environment/eightpuzzle/BidirectionalEightPuzzleProblem.cs ================================================ using System.Collections.Generic; using aima.core.search.framework.problem; namespace aima.core.environment.eightpuzzle { /** * @author Ruediger Lunde * */ public class BidirectionalEightPuzzleProblem : Problem, BidirectionalProblem { Problem reverseProblem; public BidirectionalEightPuzzleProblem(EightPuzzleBoard initialState): base(initialState, EightPuzzleFunctionFactory.getActionsFunction(), EightPuzzleFunctionFactory.getResultFunction(), new DefaultGoalTest(new EightPuzzleBoard(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }))) { reverseProblem = new Problem(new EightPuzzleBoard(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }), EightPuzzleFunctionFactory.getActionsFunction(), EightPuzzleFunctionFactory.getResultFunction(), new DefaultGoalTest(initialState)); } public Problem getOriginalProblem() { return this; } public Problem getReverseProblem() { return reverseProblem; } } } ================================================ FILE: aima-csharp/environment/eightpuzzle/EightPuzzleBoard.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.util; namespace aima.core.environment.eightpuzzle { /** * @author Ravi Mohan * @author R. Lunde */ public class EightPuzzleBoard { public static agent.Action LEFT = new DynamicAction("Left"); public static agent.Action RIGHT = new DynamicAction("Right"); public static agent.Action UP = new DynamicAction("Up"); public static agent.Action DOWN = new DynamicAction("Down"); private int[] state; // PUBLIC METHODS public EightPuzzleBoard() { state = new int[] { 5, 4, 0, 6, 1, 8, 7, 3, 2 }; } public EightPuzzleBoard(int[] state) { this.state = new int[state.Length]; System.Array.Copy(state, 0, this.state, 0, state.Length); } public EightPuzzleBoard(EightPuzzleBoard copyBoard): this(copyBoard.getState()) { } public int[] getState() { return state; } public int getValueAt(XYLocation loc) { return getValueAt(loc.getXCoOrdinate(), loc.getYCoOrdinate()); } public XYLocation getLocationOf(int val) { int absPos = getPositionOf(val); return new XYLocation(getXCoord(absPos), getYCoord(absPos)); } public void moveGapRight() { int gapPos = getGapPosition(); int x = getXCoord(gapPos); int ypos = getYCoord(gapPos); if (!(ypos == 2)) { int valueOnRight = getValueAt(x, ypos + 1); setValue(x, ypos, valueOnRight); setValue(x, ypos + 1, 0); } } public void moveGapLeft() { int gapPos = getGapPosition(); int x = getXCoord(gapPos); int ypos = getYCoord(gapPos); if (!(ypos == 0)) { int valueOnLeft = getValueAt(x, ypos - 1); setValue(x, ypos, valueOnLeft); setValue(x, ypos - 1, 0); } } public void moveGapDown() { int gapPos = getGapPosition(); int x = getXCoord(gapPos); int y = getYCoord(gapPos); if (!(x == 2)) { int valueOnBottom = getValueAt(x + 1, y); setValue(x, y, valueOnBottom); setValue(x + 1, y, 0); } } public void moveGapUp() { int gapPos = getGapPosition(); int x = getXCoord(gapPos); int y = getYCoord(gapPos); if (!(x == 0)) { int valueOnTop = getValueAt(x - 1, y); setValue(x, y, valueOnTop); setValue(x - 1, y, 0); } } public List getPositions() { List retVal = new List(); for (int i = 0; i < 9; i++) { int absPos = getPositionOf(i); XYLocation loc = new XYLocation(getXCoord(absPos), getYCoord(absPos)); retVal.Add(loc); } return retVal; } public void setBoard(List locs) { int count = 0; for (int i = 0; i < locs.Capacity; i++) { XYLocation loc = locs[i]; this.setValue(loc.getXCoOrdinate(), loc.getYCoOrdinate(), count); count = count + 1; } } public bool canMoveGap(agent.Action where) { bool retVal = true; int absPos = getPositionOf(0); if (where.Equals(LEFT)) retVal = (getYCoord(absPos) != 0); else if (where.Equals(RIGHT)) retVal = (getYCoord(absPos) != 2); else if (where.Equals(UP)) retVal = (getXCoord(absPos) != 0); else if (where.Equals(DOWN)) retVal = (getXCoord(absPos) != 2); return retVal; } public bool equals(Object o) { if (this == o) { return true; } if ((o == null) || (this.GetType() != o.GetType())) { return false; } EightPuzzleBoard aBoard = (EightPuzzleBoard)o; for (int i = 0; i < 8; i++) { if (this.getPositionOf(i) != aBoard.getPositionOf(i)) { return false; } } return true; } public int hashCode() { int result = 17; for (int i = 0; i < 8; i++) { int position = this.getPositionOf(i); result = 37 * result + position; } return result; } public String toString() { String retVal = state[0] + " " + state[1] + " " + state[2] + "\n" + state[3] + " " + state[4] + " " + state[5] + " " + "\n" + state[6] + " " + state[7] + " " + state[8]; return retVal; } // PRIVATE METHODS /** * Note: The graphic representation maps x values on row numbers (x-axis in * vertical direction). */ private int getXCoord(int absPos) { return absPos / 3; } /** * Note: The graphic representation maps y values on column numbers (y-axis * in horizontal direction). */ private int getYCoord(int absPos) { return absPos % 3; } private int getAbsPosition(int x, int y) { return x * 3 + y; } private int getValueAt(int x, int y) { // refactor this use either case or a div/mod soln return state[getAbsPosition(x, y)]; } private int getGapPosition() { return getPositionOf(0); } private int getPositionOf(int val) { int retVal = -1; for (int i = 0; i < 9; i++) { if (state[i] == val) { retVal = i; } } return retVal; } private void setValue(int x, int y, int val) { int absPos = getAbsPosition(x, y); state[absPos] = val; } } } ================================================ FILE: aima-csharp/environment/eightpuzzle/EightPuzzleFunctionFactory.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.search.framework.problem; using aima.core.util; namespace aima.core.environment.eightpuzzle { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class EightPuzzleFunctionFactory { private static ActionsFunction _actionsFunction = null; private static ResultFunction _resultFunction = null; public static ActionsFunction getActionsFunction() { if (null == _actionsFunction) { _actionsFunction = new EPActionsFunction(); } return _actionsFunction; } public static ResultFunction getResultFunction() { if (null == _resultFunction) { _resultFunction = new EPResultFunction(); } return _resultFunction; } private class EPActionsFunction : ActionsFunction { public HashSet actions(System.Object state) { EightPuzzleBoard board = (EightPuzzleBoard)state; HashSet actions = new HashSet(); if (board.canMoveGap(EightPuzzleBoard.UP)) { actions.Add(EightPuzzleBoard.UP); } if (board.canMoveGap(EightPuzzleBoard.DOWN)) { actions.Add(EightPuzzleBoard.DOWN); } if (board.canMoveGap(EightPuzzleBoard.LEFT)) { actions.Add(EightPuzzleBoard.LEFT); } if (board.canMoveGap(EightPuzzleBoard.RIGHT)) { actions.Add(EightPuzzleBoard.RIGHT); } return actions; } } private class EPResultFunction : ResultFunction { public System.Object result(System.Object s, Action a) { EightPuzzleBoard board = (EightPuzzleBoard) s; if (EightPuzzleBoard.UP.Equals(a) && board.canMoveGap(EightPuzzleBoard.UP)) { EightPuzzleBoard newBoard = new EightPuzzleBoard(board); newBoard.moveGapUp(); return newBoard; } else if (EightPuzzleBoard.DOWN.Equals(a) && board.canMoveGap(EightPuzzleBoard.DOWN)) { EightPuzzleBoard newBoard = new EightPuzzleBoard(board); newBoard.moveGapDown(); return newBoard; } else if (EightPuzzleBoard.LEFT.Equals(a) && board.canMoveGap(EightPuzzleBoard.LEFT)) { EightPuzzleBoard newBoard = new EightPuzzleBoard(board); newBoard.moveGapLeft(); return newBoard; } else if (EightPuzzleBoard.RIGHT.Equals(a) && board.canMoveGap(EightPuzzleBoard.RIGHT)) { EightPuzzleBoard newBoard = new EightPuzzleBoard(board); newBoard.moveGapRight(); return newBoard; } // The Action is not understood or is a NoOp // the result will be the current state. return s; } } } } ================================================ FILE: aima-csharp/environment/eightpuzzle/EightPuzzleGoalTest.cs ================================================ using System; using System.Collections.Generic; using aima.core.search.framework.problem; namespace aima.core.environment.eightpuzzle { /** * @author Ravi Mohan * */ public class EightPuzzleGoalTest : GoalTest { EightPuzzleBoard goal = new EightPuzzleBoard(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }); public bool isGoalState(Object state) { EightPuzzleBoard board = (EightPuzzleBoard)state; return board.Equals(goal); } } } ================================================ FILE: aima-csharp/environment/eightpuzzle/ManhattanHeuristicFunction.cs ================================================ using System; using System.Collections.Generic; using aima.core.search.framework; using aima.core.util; namespace aima.core.environment.eightpuzzle { /** * @author Ravi Mohan * */ public class ManhattanHeuristicFunction : HeuristicFunction { public double h(Object state) { EightPuzzleBoard board = (EightPuzzleBoard)state; int retVal = 0; for (int i = 1; i < 9; i++) { XYLocation loc = board.getLocationOf(i); retVal += evaluateManhattanDistanceOf(i, loc); } return retVal; } public int evaluateManhattanDistanceOf(int i, XYLocation loc) { int retVal = -1; int xpos = loc.getXCoOrdinate(); int ypos = loc.getYCoOrdinate(); switch (i) { case 1: retVal = Math.Abs(xpos - 0) + Math.Abs(ypos - 1); break; case 2: retVal = Math.Abs(xpos - 0) + Math.Abs(ypos - 2); break; case 3: retVal = Math.Abs(xpos - 1) + Math.Abs(ypos - 0); break; case 4: retVal = Math.Abs(xpos - 1) + Math.Abs(ypos - 1); break; case 5: retVal = Math.Abs(xpos - 1) + Math.Abs(ypos - 2); break; case 6: retVal = Math.Abs(xpos - 2) + Math.Abs(ypos - 0); break; case 7: retVal = Math.Abs(xpos - 2) + Math.Abs(ypos - 1); break; case 8: retVal = Math.Abs(xpos - 2) + Math.Abs(ypos - 2); break; } return retVal; } } } ================================================ FILE: aima-csharp/environment/eightpuzzle/MisplacedTilleHeuristicFunction.cs ================================================ using System; using System.Collections.Generic; using aima.core.search.framework; using aima.core.util; namespace aima.core.environment.eightpuzzle { /** * @author Ravi Mohan * */ public class MisplacedTilleHeuristicFunction : HeuristicFunction { public double h(Object state) { EightPuzzleBoard board = (EightPuzzleBoard)state; return getNumberOfMisplacedTiles(board); } private int getNumberOfMisplacedTiles(EightPuzzleBoard board) { int numberOfMisplacedTiles = 0; if (!(board.getLocationOf(0).Equals(new XYLocation(0, 0)))) { numberOfMisplacedTiles++; } if (!(board.getLocationOf(1).Equals(new XYLocation(0, 1)))) { numberOfMisplacedTiles++; } if (!(board.getLocationOf(2).Equals(new XYLocation(0, 2)))) { numberOfMisplacedTiles++; } if (!(board.getLocationOf(3).Equals(new XYLocation(1, 0)))) { numberOfMisplacedTiles++; } if (!(board.getLocationOf(4).Equals(new XYLocation(1, 1)))) { numberOfMisplacedTiles++; } if (!(board.getLocationOf(5).Equals(new XYLocation(1, 2)))) { numberOfMisplacedTiles++; } if (!(board.getLocationOf(6).Equals(new XYLocation(2, 0)))) { numberOfMisplacedTiles++; } if (!(board.getLocationOf(7).Equals(new XYLocation(2, 1)))) { numberOfMisplacedTiles++; } if (!(board.getLocationOf(8).Equals(new XYLocation(2, 2)))) { numberOfMisplacedTiles++; } return numberOfMisplacedTiles; } } } ================================================ FILE: aima-csharp/environment/map/AdaptableHeuristicFunction.cs ================================================ using System; using System.Collections.Generic; using aima.core.search.framework; namespace aima.core.environment.map { /** * This class extends heuristic functions in two ways: It maintains a goal and a * map to estimate distance to goal for states in route planning problems, and * it provides a method to adapt to different goals. * * @author Ruediger Lunde */ public abstract class AdaptableHeuristicFunction { /** The Current Goal. */ protected Object goal; /** The map to be used for distance to goal estimates. */ protected Map map; /** * Modifies goal and map information and returns the modified heuristic * function. */ public AdaptableHeuristicFunction adaptToGoal(Object goal, Map map) { this.goal = goal; this.map = map; return this; } // when subclassing: Don't forget to implement the most important method // public double h(Object state) } } ================================================ FILE: aima-csharp/environment/map/BidirectionalMapProblem.cs ================================================ using System; using System.Collections.Generic; using aima.core.search.framework.problem; namespace aima.core.environment.map { /** * @author Ciaran O'Reilly * */ public class BidirectionalMapProblem : Problem, BidirectionalProblem { Map map; Problem reverseProblem; public BidirectionalMapProblem(Map map, String initialState, String goalState): this(map, initialState, goalState, new DefaultGoalTest(goalState)) { } public BidirectionalMapProblem(Map map, String initialState, String goalState, GoalTest goalTest) : base(initialState, MapFunctionFactory.getActionsFunction(map), MapFunctionFactory.getResultFunction(), goalTest, new MapStepCostFunction(map)) { this.map = map; reverseProblem = new Problem(goalState, MapFunctionFactory.getReverseActionsFunction(map), MapFunctionFactory.getResultFunction(), new DefaultGoalTest(initialState), new MapStepCostFunction(map)); } // START Interface BidrectionalProblem public Problem getOriginalProblem() { return this; } public Problem getReverseProblem() { return reverseProblem; } // END Interface BirectionalProblem } } ================================================ FILE: aima-csharp/environment/map/DynAttributeNames.cs ================================================ using System; namespace aima.core.environment.map { /** * The AIMA framework uses dynamic attributes to make implementations of agents * and environments completely independent of each other. The disadvantage of * this concept is, that it's error-prone. This set of constants is designed to * make information exchange more reliable for map agents. Two kinds of * attributes are distinguished. Percept attributes are attached to percepts. * They are generated by the environment and read by by the agent. * EnvironmentState attributes are attached to the EnvironmentState of the * Environment. * * @author Ruediger Lunde */ public class DynAttributeNames { /** * Name of a dynamic attribute, which contains the current location of the * agent. Expected value type: String. */ public static readonly String AGENT_LOCATION = "location"; /** * Name of a dynamic attribute, which tells the agent where it is. Expected * value type: String. */ public static readonly String PERCEPT_IN = "in"; } } ================================================ FILE: aima-csharp/environment/map/ExtendableMap.cs ================================================ using System; using System.Collections.Generic; using aima.core.util; namespace aima.core.environment.map { /** * Implements a map with locations, distance labeled links between the * locations, straight line distances, and 2d-placement positions of locations. * Locations are represented by strings and travel distances by double values. * Locations and links can be added dynamically and removed after creation. This * enables to read maps from file or to modify them with respect to newly * obtained knowledge. * * @author Ruediger Lunde */ public class ExtendableMap : Map { /** * Stores map data. Locations are represented as vertices and connections * (links) as directed edges labeled with corresponding travel distances. */ private readonly LabeledGraph links; /** Stores xy-coordinates for each location. */ private readonly Dictionary locationPositions; /** Creates an empty map. */ public ExtendableMap() { links = new LabeledGraph(); locationPositions = new Dictionary(); } /** Removes everything. */ public void clear() { links.clear(); locationPositions.Clear(); } /** Clears all connections but keeps location position informations. */ public void clearLinks() { links.clear(); } /** Returns a list of all locations. */ public List getLocations() { return links.getVertexLabels(); } /** Checks whether the given string is the name of a location. */ public bool isLocation(String str) { return links.isVertexLabel(str); } /** * Answers to the question: Where can I get, following one of the * connections starting at the specified location? */ public List getPossibleNextLocations(String location) { List result = links.getSuccessors(location); result.Sort(); return result; } /** * Answers to the question: From where can I reach a specified location, * following one of the map connections? This implementation just calls * {@link #getPossibleNextLocations(String)} as the underlying graph structure * cannot be traversed efficiently in reverse order. */ public List getPossiblePrevLocations(String location) { return getPossibleNextLocations(location); } /** * Returns the travel distance between the two specified locations if they * are linked by a connection and null otherwise. */ public Double getDistance(String fromLocation, String toLocation) { return links.get(fromLocation, toLocation); } /** Adds a one-way connection to the map. */ public void addUnidirectionalLink(String fromLocation, String toLocation, Double distance) { links.set(fromLocation, toLocation, distance); } /** * Adds a connection which can be traveled in both direction. Internally, * such a connection is represented as two one-way connections. */ public void addBidirectionalLink(String fromLocation, String toLocation, Double distance) { links.set(fromLocation, toLocation, distance); links.set(toLocation, fromLocation, distance); } /** * Returns a location which is selected by random. */ public String randomlyGenerateDestination() { return Util.selectRandomlyFromList(getLocations()); } /** Removes a one-way connection. */ public void removeUnidirectionalLink(String fromLocation, String toLocation) { links.remove(fromLocation, toLocation); } /** Removes the two corresponding one-way connections. */ public void removeBidirectionalLink(String fromLocation, String toLocation) { links.remove(fromLocation, toLocation); links.remove(toLocation, fromLocation); } /** * Defines the position of a location as with respect to an orthogonal * coordinate system. */ public void setPosition(String loc, double x, double y) { locationPositions.Add(loc, new Point2D(x, y)); } /** * Defines the position of a location within the map.Using this method, one * location should be selected as reference position (dist= 0 *and < code > dir = 0 ) and all the other location should be placed * relative to it. * * @param loc * location name * @param dist * distance to a reference position * @param dir * bearing (compass direction) in which the location is seen from * the reference position */ public void setDistAndDirToRefLocation(String loc, double dist, int dir) { Point2D coords = new Point2D(-Math.Sin(dir * Math.PI / 180.0) * dist, Math.Cos(dir * Math.PI / 180.0) * dist); links.addVertex(loc); locationPositions.Add(loc, coords); } /** * Returns the position of the specified location as with respect to an * orthogonal coordinate system. */ public Point2D getPosition(String loc) { return locationPositions[loc]; } } } ================================================ FILE: aima-csharp/environment/map/Map.cs ================================================ using System; using System.Collections.Generic; using aima.core.util; namespace aima.core.environment.map { /** * Provides a general interface for maps. * * @author Ruediger Lunde */ public interface Map { /** Returns a list of all locations. */ List getLocations(); /** * Answers to the question: Where can I get, following one of the * connections starting at the specified location? */ List getPossibleNextLocations(String location); /** * Answers to the question: From where can I reach a specified location, * following one of the map connections? */ List getPossiblePrevLocations(String location); /** * Returns the travel distance between the two specified locations if they * are linked by a connection and null otherwise. */ Double getDistance(String fromLocation, String toLocation); /** * Returns the position of the specified location. The position is * represented by two coordinates, e.g. latitude and longitude values. */ Point2D getPosition(String loc); /** * Returns a location which is selected by random. */ String randomlyGenerateDestination(); } } ================================================ FILE: aima-csharp/environment/map/MapAgent.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.search.framework; using aima.core.search.framework.problem; namespace aima.core.environment.map { /** * @author Ciaran O'Reilly * */ public class MapAgent : SimpleProblemSolvingAgent { protected Map map = null; protected DynamicState state = new DynamicState(); private EnvironmentViewNotifier notifier = null; private Search _search = null; private String[] goals = null; private int goalTestPos = 0; public MapAgent(Map map, EnvironmentViewNotifier notifier, Search search) { this.map = map; this.notifier = notifier; this._search = search; } public MapAgent(Map map, EnvironmentViewNotifier notifier, Search search, int maxGoalsToFormulate): base(maxGoalsToFormulate) { this.map = map; this.notifier = notifier; this._search = search; } public MapAgent(Map map, EnvironmentViewNotifier notifier, Search search, String[] goals): base(goals.Length) { this.map = map; this.notifier = notifier; this._search = search; this.goals = new String[goals.Length]; Array.Copy(goals, 0, this.goals, 0, goals.Length); } // PROTECTED METHODS protected override State updateState(Percept p) { DynamicPercept dp = (DynamicPercept)p; state.setAttribute(DynAttributeNames.AGENT_LOCATION, dp.getAttribute(DynAttributeNames.PERCEPT_IN)); return state; } protected override Object formulateGoal() { Object goal = null; if (null == goals) { goal = map.randomlyGenerateDestination(); } else { goal = goals[goalTestPos]; goalTestPos++; } notifier.notifyViews("CurrentLocation=In(" + state.getAttribute(DynAttributeNames.AGENT_LOCATION) + "), Goal=In(" + goal + ")"); return goal; } protected override Problem formulateProblem(Object goal) { return new BidirectionalMapProblem(map, (String)state.getAttribute(DynAttributeNames.AGENT_LOCATION), (String)goal); } protected override List search(Problem problem) { List actions = new List(); try { List sactions = _search.search(problem); foreach (agent.Action action in sactions) { actions.Add(action); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.ToString()); } return actions; } protected override void notifyViewOfMetrics() { HashSet keys = _search.getMetrics().keySet(); foreach (String key in keys) { notifier.notifyViews("METRIC[" + key + "]=" + _search.getMetrics().get(key)); } } } } ================================================ FILE: aima-csharp/environment/map/MapEnvironment.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; namespace aima.core.environment.map { /** * Represents the environment a MapAgent can navigate. * * @author Ciaran O'Reilly * */ public class MapEnvironment : AbstractEnvironment { private Map map = null; private MapEnvironmentState state = new MapEnvironmentState(); public MapEnvironment(Map map) { this.map = map; } public void addAgent(Agent a, string startLocation) { // Ensure the agent state information is tracked before // adding to super, as super will notify the registered // EnvironmentViews that is was added. state.setAgentLocationAndTravelDistance(a, startLocation, 0.0); base.addAgent(a); } public string getAgentLocation(Agent a) { return state.getAgentLocation(a); } public double getAgentTravelDistance(Agent a) { return state.getAgentTravelDistance(a); } public override EnvironmentState getCurrentState() { return state; } public override EnvironmentState executeAction(Agent agent, Action a) { if (!a.isNoOp()) { MoveToAction act = (MoveToAction)a; string currLoc = getAgentLocation(agent); double distance = map.getDistance(currLoc, act.getToLocation()); if (distance != null) { double currTD = getAgentTravelDistance(agent); state.setAgentLocationAndTravelDistance(agent, act.getToLocation(), currTD + distance); } } return state; } public override Percept getPerceptSeenBy(Agent anAgent) { return new DynamicPercept(DynAttributeNames.PERCEPT_IN, getAgentLocation(anAgent)); } public Map getMap() { return map; } } } ================================================ FILE: aima-csharp/environment/map/MapEnvironmentState.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.util; namespace aima.core.environment.map { /** * @author Ciaran O'Reilly * */ public class MapEnvironmentState : EnvironmentState { private Dictionary> agentLocationAndTravelDistance = new Dictionary>(); public MapEnvironmentState() { } public string getAgentLocation(Agent a) { Pair locAndTDistance = agentLocationAndTravelDistance[a]; if (null == locAndTDistance) { return null; } return locAndTDistance.getFirst(); } public double getAgentTravelDistance(Agent a) { Pair locAndTDistance = agentLocationAndTravelDistance[a]; if (null == locAndTDistance) { return double.MinValue; } return locAndTDistance.getSecond(); } public void setAgentLocationAndTravelDistance(Agent a, string location, double travelDistance) { agentLocationAndTravelDistance.Add(a, new Pair( location, travelDistance)); } } } ================================================ FILE: aima-csharp/environment/map/MapFunctionFactory.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.search.framework; using aima.core.search.framework.problem; using aima.core.util; namespace aima.core.environment.map { /** * @author Ciaran O'Reilly * */ public class MapFunctionFactory { private static ResultFunction resultFunction; private static PerceptToStateFunction perceptToStateFunction; public static ActionsFunction getActionsFunction(Map map) { return new MapActionsFunction(map, false); } public static ActionsFunction getReverseActionsFunction(Map map) { return new MapActionsFunction(map, true); } public static ResultFunction getResultFunction() { if (null == resultFunction) { resultFunction = new MapResultFunction(); } return resultFunction; } private class MapActionsFunction : ActionsFunction { private Map map = null; private bool reverseMode; public MapActionsFunction(Map map, bool reverseMode) { this.map = map; this.reverseMode = reverseMode; } public HashSet actions(System.Object state) { HashSet actions = new HashSet(); System.String location = state.ToString(); List linkedLocations = reverseMode ? map.getPossiblePrevLocations(location) : map.getPossibleNextLocations(location); foreach (System.String linkLoc in linkedLocations) { actions.Add(new MoveToAction(linkLoc)); } return actions; } } public static PerceptToStateFunction getPerceptToStateFunction() { if (null == perceptToStateFunction) { perceptToStateFunction = new MapPerceptToStateFunction(); } return perceptToStateFunction; } private class MapResultFunction : ResultFunction { public MapResultFunction() { } public System.Object result(System.Object s, Action a) { if (a is MoveToAction) { MoveToAction mta = (MoveToAction)a; return mta.getToLocation(); } // The Action is not understood or is a NoOp // the result will be the current state. return s; } } private class MapPerceptToStateFunction : PerceptToStateFunction { public System.Object getState(Percept p) { return ((DynamicPercept)p) .getAttribute(DynAttributeNames.PERCEPT_IN); } } } } ================================================ FILE: aima-csharp/environment/map/MapStepCostFunction.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.search.framework.problem; namespace aima.core.environment.map { /** * Implementation of StepCostFunction interface that uses the distance between * locations to calculate the cost in addition to a constant cost, so that it * may be used in conjunction with a Uniform-cost search. * * @author Ciaran O'Reilly * */ public class MapStepCostFunction : StepCostFunction { private Map map = null; // Used by Uniform-cost search to ensure every step is greater than or equal // to some small positive constant private static double constantCost = 1.0; public MapStepCostFunction(Map map) { this.map = map; } // // START-StepCostFunction public double c(object fromCurrentState, Action action, object toNextState) { string fromLoc = fromCurrentState.ToString(); string toLoc = toNextState.ToString(); double distance = map.getDistance(fromLoc, toLoc); if (distance == null || distance <= 0) { return constantCost; } return distance; } // END-StepCostFunction } } ================================================ FILE: aima-csharp/environment/map/MoveToAction.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent.impl; namespace aima.core.environment.map { public class MoveToAction : DynamicAction { public const String ATTRIBUTE_MOVE_TO_LOCATION = "location"; public MoveToAction(String location) : base("moveTo") { setAttribute(ATTRIBUTE_MOVE_TO_LOCATION, location); } public String getToLocation() { return (String)getAttribute(ATTRIBUTE_MOVE_TO_LOCATION); } } } ================================================ FILE: aima-csharp/environment/map/Scenario.cs ================================================ using System; namespace aima.core.environment.map { /** * A scenario specifies an environment, the agent's knowledge about the * environment, and the agents initial location. It can be used to specify * settings for route planning agent applications. * * @author Ruediger Lunde */ public class Scenario { /** * A map-based environment. Note that the contained map must be of type * {@link ExtendableMap}. */ private readonly MapEnvironment env; /** A map reflecting the knowledge of the agent about the environment. */ private readonly Map agentMap; /** Initial location of the agent. */ private readonly String initAgentLoc; /** * Creates a scenario. * * @param env * a map-based environment. Note that the contained map must be * of type {@link ExtendableMap} * @param agentMap * a map reflecting the knowledge of the agent about the * environment * @param agentLoc * initial location of the agent */ public Scenario(MapEnvironment env, Map agentMap, String agentLoc) { this.agentMap = agentMap; this.env = env; this.initAgentLoc = agentLoc; } public MapEnvironment getEnv() { return env; } public Map getEnvMap() { return env.getMap(); } public Map getAgentMap() { return agentMap; } public String getInitAgentLocation() { return initAgentLoc; } } } ================================================ FILE: aima-csharp/environment/map/SimplifiedRoadMapOfAustralia.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.environment.map { /** * Represents a simplified road map of Australia. The initialization method is * declared static. So it can also be used to initialize other specialized * subclasses of {@link ExtendableMap} with road map data from Australia. The * data was extracted from a class developed by Felix Knittel. * * @author Ruediger Lunde */ public class SimplifiedRoadMapOfAustralia : ExtendableMap { public SimplifiedRoadMapOfAustralia() { initMap(this); } // Locations public const String ADELAIDE = "Adelaide"; public const String ALBANY = "Albany"; public const String ALICE_SPRINGS = "AliceSprings"; public const String BRISBANE = "Brisbane"; public const String BROKEN_HILL = "BrokenHill"; public const String BROOME = "Broome"; public const String CAIRNS = "Cairns"; public const String CAMARVON = "Camarvon"; public const String CANBERRA = "Canberra"; public const String CHARLEVILLE = "Charleville"; public const String COOBER_PEDY = "CooberPedy"; public const String DARWIN = "Darwin"; public const String DUBBO = "Dubbo"; public const String ESPERANCE = "Esperance"; public const String GERALDTON = "Geraldton"; public const String HALLS_CREEK = "HallsCreek"; public const String HAY = "Hay"; public const String KALGOORLIE = "Kalgoorlie"; public const String KATHERINE = "Katherine"; public const String LAKES_ENTRANCE = "LakesEntrance"; public const String LONGREACH = "Longreach"; public const String MACKAY = "Mackay"; public const String MELBOURNE = "Melbourne"; public const String MOUNT_GAMBIER = "MountGambier"; public const String MT_ISA = "MtIsa"; public const String NEWCASTLE = "Newcastle"; public const String NORSEMAN = "Norseman"; public const String NYNGAN = "Nyngan"; public const String PERTH = "Perth"; public const String PORT_AUGUSTA = "PortAugusta"; public const String PORT_HEDLAND = "PortHedland"; public const String PORT_LINCOLN = "PortLincoln"; public const String PORT_MACQUARIE = "PortMacquarie"; public const String ROCKHAMPTON = "Rockhampton"; public const String SYDNEY = "Sydney"; public const String TAMWORTH = "Tamworth"; public const String TENNANT_CREEK = "TennantCreek"; public const String TOWNSVILLE = "Townsville"; public const String WAGGA_WAGGA = "WaggaWagga"; public const String WARNAMBOOL = "Warnambool"; public const String WYNDHAM = "Wyndham"; /** * Initializes a map with a simplified road map of Australia. */ public static void initMap(ExtendableMap map) { map.clear(); // Add links // Distances from http://maps.google.com map.addBidirectionalLink(PERTH, ALBANY, 417.0); map.addBidirectionalLink(PERTH, KALGOORLIE, 593.0); map.addBidirectionalLink(PERTH, GERALDTON, 424.0); map.addBidirectionalLink(PERTH, PORT_HEDLAND, 1637.0); map.addBidirectionalLink(ALBANY, ESPERANCE, 478.0); map.addBidirectionalLink(KALGOORLIE, NORSEMAN, 187.0); map.addBidirectionalLink(ESPERANCE, NORSEMAN, 204.0); map.addBidirectionalLink(NORSEMAN, PORT_AUGUSTA, 1668.0); map.addBidirectionalLink(GERALDTON, CAMARVON, 479.0); map.addBidirectionalLink(CAMARVON, PORT_HEDLAND, 872.0); map.addBidirectionalLink(PORT_HEDLAND, BROOME, 589.0); map.addBidirectionalLink(BROOME, HALLS_CREEK, 685.0); map.addBidirectionalLink(HALLS_CREEK, WYNDHAM, 370.0); map.addBidirectionalLink(HALLS_CREEK, KATHERINE, 874.0); map.addBidirectionalLink(WYNDHAM, KATHERINE, 613.0); map.addBidirectionalLink(KATHERINE, DARWIN, 317.0); map.addBidirectionalLink(KATHERINE, TENNANT_CREEK, 673.0); map.addBidirectionalLink(TENNANT_CREEK, MT_ISA, 663.0); map.addBidirectionalLink(TENNANT_CREEK, ALICE_SPRINGS, 508.0); map.addBidirectionalLink(ALICE_SPRINGS, COOBER_PEDY, 688.0); map.addBidirectionalLink(COOBER_PEDY, PORT_AUGUSTA, 539.0); map.addBidirectionalLink(MT_ISA, TOWNSVILLE, 918.0); map.addBidirectionalLink(TOWNSVILLE, CAIRNS, 346.0); map.addBidirectionalLink(MT_ISA, LONGREACH, 647.0); map.addBidirectionalLink(TOWNSVILLE, MACKAY, 388.0); map.addBidirectionalLink(MACKAY, ROCKHAMPTON, 336.0); map.addBidirectionalLink(LONGREACH, ROCKHAMPTON, 687.0); map.addBidirectionalLink(ROCKHAMPTON, BRISBANE, 616.0); map.addBidirectionalLink(LONGREACH, CHARLEVILLE, 515.0); map.addBidirectionalLink(CHARLEVILLE, BRISBANE, 744.0); map.addBidirectionalLink(CHARLEVILLE, NYNGAN, 657.0); map.addBidirectionalLink(NYNGAN, BROKEN_HILL, 588.0); map.addBidirectionalLink(BROKEN_HILL, PORT_AUGUSTA, 415.0); map.addBidirectionalLink(NYNGAN, DUBBO, 166.0); map.addBidirectionalLink(DUBBO, BRISBANE, 860.0); map.addBidirectionalLink(DUBBO, SYDNEY, 466.0); map.addBidirectionalLink(BRISBANE, TAMWORTH, 576.0); map.addBidirectionalLink(BRISBANE, PORT_MACQUARIE, 555.0); map.addBidirectionalLink(PORT_MACQUARIE, NEWCASTLE, 245.0); map.addBidirectionalLink(TAMWORTH, NEWCASTLE, 284.0); map.addBidirectionalLink(NEWCASTLE, SYDNEY, 159.0); map.addBidirectionalLink(SYDNEY, CANBERRA, 287.0); map.addBidirectionalLink(CANBERRA, WAGGA_WAGGA, 243.0); map.addBidirectionalLink(DUBBO, WAGGA_WAGGA, 400.0); map.addBidirectionalLink(SYDNEY, LAKES_ENTRANCE, 706.0); map.addBidirectionalLink(LAKES_ENTRANCE, MELBOURNE, 317.0); map.addBidirectionalLink(WAGGA_WAGGA, MELBOURNE, 476.0); map.addBidirectionalLink(WAGGA_WAGGA, HAY, 269.0); map.addBidirectionalLink(MELBOURNE, WARNAMBOOL, 269.0); map.addBidirectionalLink(WARNAMBOOL, MOUNT_GAMBIER, 185.0); map.addBidirectionalLink(MOUNT_GAMBIER, ADELAIDE, 449.0); map.addBidirectionalLink(HAY, ADELAIDE, 655.0); map.addBidirectionalLink(PORT_AUGUSTA, ADELAIDE, 306.0); map.addBidirectionalLink(MELBOURNE, ADELAIDE, 728.0); map.addBidirectionalLink(PORT_AUGUSTA, PORT_LINCOLN, 341.0); // Locations coordinates // Alice Springs is taken as central point with coordinates (0|0) // Therefore x and y coordinates refer to Alice Springs. Note that // the coordinates are not very precise and partly modified to // get a more real shape of Australia. map.setPosition(ADELAIDE, 417, 1289); map.setPosition(ALBANY, -1559, 1231); map.setPosition(ALICE_SPRINGS, 0, 0); map.setPosition(BRISBANE, 1882, 415); map.setPosition(BROKEN_HILL, 709, 873); map.setPosition(BROOME, -1189, -645); map.setPosition(CAIRNS, 1211, -791); map.setPosition(CAMARVON, -2004, -34); map.setPosition(CANBERRA, 1524, 1189); map.setPosition(CHARLEVILLE, 1256, 268); map.setPosition(COOBER_PEDY, 86, 593); map.setPosition(DARWIN, -328, -1237); map.setPosition(DUBBO, 1474, 881); map.setPosition(ESPERANCE, -1182, 1132); map.setPosition(GERALDTON, -1958, 405); map.setPosition(HALLS_CREEK, -630, -624); map.setPosition(HAY, 985, 1143); map.setPosition(KALGOORLIE, -1187, 729); map.setPosition(KATHERINE, -183, -1025); map.setPosition(LAKES_ENTRANCE, 1412, 1609); map.setPosition(LONGREACH, 1057, -49); map.setPosition(MACKAY, 1553, -316); map.setPosition(MELBOURNE, 1118, 1570); map.setPosition(MOUNT_GAMBIER, 602, 1531); map.setPosition(MT_ISA, 563, -344); map.setPosition(NEWCASTLE, 1841, 979); map.setPosition(NORSEMAN, -1162, 881); map.setPosition(NYNGAN, 1312, 781); map.setPosition(PERTH, -1827, 814); map.setPosition(PORT_AUGUSTA, 358, 996); map.setPosition(PORT_HEDLAND, -1558, -438); map.setPosition(PORT_LINCOLN, 169, 1205); map.setPosition(PORT_MACQUARIE, 1884, 849); map.setPosition(ROCKHAMPTON, 1693, -59); map.setPosition(SYDNEY, 1778, 1079); map.setPosition(TAMWORTH, 1752, 722); map.setPosition(TENNANT_CREEK, 30, -445); map.setPosition(TOWNSVILLE, 1318, -520); map.setPosition(WAGGA_WAGGA, 1322, 1125); map.setPosition(WARNAMBOOL, 761, 1665); map.setPosition(WYNDHAM, -572, -932); } } } ================================================ FILE: aima-csharp/environment/map/SimplifiedRoadMapOfPartOfRomania.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.environment.map { /** * Represents a simplified road map of Romania. The initialization method is * declared static. So it can also be used to initialize other specialized * subclasses of {@link ExtendableMap} with road map data from Romania. Location * names, road distances and directions have been extracted from Artificial * Intelligence A Modern Approach (2nd Edition), Figure 3.2, page 63. The * straight-line distances to Bucharest have been taken from Artificial * Intelligence A Modern Approach (2nd Edition), Figure 4.1, page 95. * * @author Ruediger Lunde */ public class SimplifiedRoadMapOfPartOfRomania : ExtendableMap { public SimplifiedRoadMapOfPartOfRomania() { initMap(this); } // The different locations in the simplified map of part of Romania public const String ORADEA = "Oradea"; public const String ZERIND = "Zerind"; public const String ARAD = "Arad"; public const String TIMISOARA = "Timisoara"; public const String LUGOJ = "Lugoj"; public const String MEHADIA = "Mehadia"; public const String DOBRETA = "Dobreta"; public const String SIBIU = "Sibiu"; public const String RIMNICU_VILCEA = "RimnicuVilcea"; public const String CRAIOVA = "Craiova"; public const String FAGARAS = "Fagaras"; public const String PITESTI = "Pitesti"; public const String GIURGIU = "Giurgiu"; public const String BUCHAREST = "Bucharest"; public const String NEAMT = "Neamt"; public const String URZICENI = "Urziceni"; public const String IASI = "Iasi"; public const String VASLUI = "Vaslui"; public const String HIRSOVA = "Hirsova"; public const String EFORIE = "Eforie"; /** * Initializes a map with a simplified road map of Romania. */ public static void initMap(ExtendableMap map) { // mapOfRomania map.clear(); map.addBidirectionalLink(ORADEA, ZERIND, 71.0); map.addBidirectionalLink(ORADEA, SIBIU, 151.0); map.addBidirectionalLink(ZERIND, ARAD, 75.0); map.addBidirectionalLink(ARAD, TIMISOARA, 118.0); map.addBidirectionalLink(ARAD, SIBIU, 140.0); map.addBidirectionalLink(TIMISOARA, LUGOJ, 111.0); map.addBidirectionalLink(LUGOJ, MEHADIA, 70.0); map.addBidirectionalLink(MEHADIA, DOBRETA, 75.0); map.addBidirectionalLink(DOBRETA, CRAIOVA, 120.0); map.addBidirectionalLink(SIBIU, FAGARAS, 99.0); map.addBidirectionalLink(SIBIU, RIMNICU_VILCEA, 80.0); map.addBidirectionalLink(RIMNICU_VILCEA, PITESTI, 97.0); map.addBidirectionalLink(RIMNICU_VILCEA, CRAIOVA, 146.0); map.addBidirectionalLink(CRAIOVA, PITESTI, 138.0); map.addBidirectionalLink(FAGARAS, BUCHAREST, 211.0); map.addBidirectionalLink(PITESTI, BUCHAREST, 101.0); map.addBidirectionalLink(GIURGIU, BUCHAREST, 90.0); map.addBidirectionalLink(BUCHAREST, URZICENI, 85.0); map.addBidirectionalLink(NEAMT, IASI, 87.0); map.addBidirectionalLink(URZICENI, VASLUI, 142.0); map.addBidirectionalLink(URZICENI, HIRSOVA, 98.0); map.addBidirectionalLink(IASI, VASLUI, 92.0); // addBidirectionalLink(VASLUI - already all linked map.addBidirectionalLink(HIRSOVA, EFORIE, 86.0); // addBidirectionalLink(EFORIE - already all linked // distances and directions // reference location: Bucharest map.setDistAndDirToRefLocation(ARAD, 366, 117); map.setDistAndDirToRefLocation(BUCHAREST, 0, 360); map.setDistAndDirToRefLocation(CRAIOVA, 160, 74); map.setDistAndDirToRefLocation(DOBRETA, 242, 82); map.setDistAndDirToRefLocation(EFORIE, 161, 282); map.setDistAndDirToRefLocation(FAGARAS, 176, 142); map.setDistAndDirToRefLocation(GIURGIU, 77, 25); map.setDistAndDirToRefLocation(HIRSOVA, 151, 260); map.setDistAndDirToRefLocation(IASI, 226, 202); map.setDistAndDirToRefLocation(LUGOJ, 244, 102); map.setDistAndDirToRefLocation(MEHADIA, 241, 92); map.setDistAndDirToRefLocation(NEAMT, 234, 181); map.setDistAndDirToRefLocation(ORADEA, 380, 131); map.setDistAndDirToRefLocation(PITESTI, 100, 116); map.setDistAndDirToRefLocation(RIMNICU_VILCEA, 193, 115); map.setDistAndDirToRefLocation(SIBIU, 253, 123); map.setDistAndDirToRefLocation(TIMISOARA, 329, 105); map.setDistAndDirToRefLocation(URZICENI, 80, 247); map.setDistAndDirToRefLocation(VASLUI, 199, 222); map.setDistAndDirToRefLocation(ZERIND, 374, 125); } } } ================================================ FILE: aima-csharp/environment/map/StraightLineDistanceHeuristicFunction.cs ================================================ using System; using aima.core.util; namespace aima.core.environment.map { /** * @author Ruediger Lunde */ public class StraightLineDistanceHeuristicFunction : AdaptableHeuristicFunction { public StraightLineDistanceHeuristicFunction(Object goal, Map map) { this.goal = goal; this.map = map; } public double h(Object state) { Double result = 0.0; Point2D pt1 = map.getPosition((String)state); Point2D pt2 = map.getPosition((String)goal); if (pt1 != null && pt2 != null) { result = pt1.distance(pt2); } return result; } } } ================================================ FILE: aima-csharp/environment/wumpusworld/AgentPercept.cs ================================================ using System.Collections.Generic; using aima.core.agent; namespace aima.core.environment.wumpusworld { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 237.
*
* The agent has five sensors, each of which gives a single bit of information: *
    *
  • In the square containing the wumpus and in the directly (not diagonally) * adjacent squares, the agent will perceive a Stench.
  • *
  • In the squares directly adjacent to a pit, the agent will perceive a * Breeze.
  • *
  • In the square where the gold is, the agent will perceive a Glitter.
  • *
  • When an agent walks into a wall, it will perceive a Bump.
  • *
  • When the wumpus is killed, it emits a woeful Scream that can be perceived * anywhere in the cave.
  • *
* * @author Federico Baron * @author Alessandro Daniele * @author Ciaran O'Reilly */ public class AgentPercept : Percept { private bool stench; private bool breeze; private bool glitter; private bool bump; private bool scream; /** * Default Constructor. All sensor inputs are considered false. */ public AgentPercept() { setStench(false); setBreeze(false); setGlitter(false); setBump(false); setScream(false); } /** * Constructor with all 5 sensor inputs explicitly set. * * @param stench * @param breeze * @param glitter * @param bump * @param scream */ public AgentPercept(bool stench, bool breeze, bool glitter, bool bump, bool scream) { setStench(stench); setBreeze(breeze); setGlitter(glitter); setBump(bump); setScream(scream); } public bool isStench() { return stench; } public void setStench(bool stench) { this.stench = stench; } public bool isBreeze() { return breeze; } public void setBreeze(bool breeze) { this.breeze = breeze; } public bool isGlitter() { return glitter; } public void setGlitter(bool glitter) { this.glitter = glitter; } public bool isBump() { return bump; } public void setBump(bool bump) { this.bump = bump; } public bool isScream() { return scream; } public void setScream(bool scream) { this.scream = scream; } public string toString() { return "[" + ((stench) ? "Stench" : "None") + ", " + ((breeze) ? "Breeze" : "None") + ", " + ((glitter) ? "Glitter" : "None") + ", " + ((bump) ? "Bump" : "None") + ", " + ((scream) ? "Scream" : "None") + "]"; } } } ================================================ FILE: aima-csharp/environment/wumpusworld/AgentPosition.cs ================================================ using System; namespace aima.core.environment.wumpusworld { internal class AgentPosition { internal int getX() { throw new NotImplementedException(); } internal int getY() { throw new NotImplementedException(); } } } ================================================ FILE: aima-csharp/environment/wumpusworld/ManhattanHeuristicFunction.cs ================================================ using System; using System.Collections.Generic; using aima.core.search.framework; namespace aima.core.environment.wumpusworld { /** * Heuristic for calculating the Manhattan distance between two rooms within a Wumpus World cave. * * @author Federico Baron * @author Alessandro Daniele * @author Ciaran O'Reilly */ public class ManhattanHeuristicFunction : HeuristicFunction { List goals = new List(); public ManhattanHeuristicFunction(HashSet goals) { this.goals.AddRange(goals); } public double h(Object state) { AgentPosition pos = (AgentPosition)state; int nearestGoalDist = int.MaxValue; foreach (Room g in goals) { int tmp = evaluateManhattanDistanceOf(pos.getX(), pos.getY(), g.getX(), g.getY()); if (tmp < nearestGoalDist) { nearestGoalDist = tmp; } } return nearestGoalDist; } // PRIVATE private int evaluateManhattanDistanceOf(int x1, int y1, int x2, int y2) { return Math.Abs(x1 - x2) + Math.Abs(y1 - y2); } } } ================================================ FILE: aima-csharp/environment/wumpusworld/Room.cs ================================================ using System.Collections.Generic; namespace aima.core.environment.wumpusworld { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 236.
*
* The wumpus world is a cave consisting of rooms connected by * passageways. Rooms are labeled [x,y], for example [1,1] would indicate the * room in the bottom left, and is also the room the agent always starts in. See * Figure 7.2 for an example room layout representing a wumpus's cave. * * @author Ciaran O'Reilly */ public class Room { private int x = 1; private int y = 1; /** * Constructor. * * @param x * the room's x location. * @param y * the room's y location. */ public Room(int x, int y) { this.x = x; this.y = y; } /** * * @return the room's x location. */ public int getX() { return x; } /** * * @return the room's y location. */ public int getY() { return y; } public string toString() { return "[" + x + "," + y + "]"; } public bool equals(object o) { if (o != null && o is Room) { Room r = (Room)o; if (x == r.x && y == r.y) { return true; } return false; } return false; } public int hashCode() { int result = 17; result = 37 * result + getX(); result = 43 * result + getY(); return result; } } } ================================================ FILE: aima-csharp/logic/common/Lexer.cs ================================================ using System; using System.Collections.Generic; using System.IO; namespace aima.core.logic.common { /** * An abstract base class for constructing lexical analyzers for knowledge * representation languages. It provides a mechanism for converting a sequence * of characters to a sequence of tokens that are meaningful in the * representation language of interest. * * @author Ravi Mohan * @author Ciaran O'Reilly * @author Mike Stampone */ public abstract class Lexer { protected int lookAheadBufferSize = 1; // private static readonly int END_OF_INPUT = -1; // private StringReader input; public int[] lookAheadBuffer; private int currentPositionInInput; /** * Sets the character stream of the lexical analyzer. * * @param inputString * a sequence of characters to be converted into a sequence of * tokens. */ public void setInput(String inputString) { this.input = new StringReader(inputString); } /** * Set the character stream reader of the lexical analyzer. * * @param inputReader * a reader on a sequence of characters to be converted into a * sequence of tokens. */ public void setInput(StringReader inputReader) { input = inputReader; lookAheadBuffer = new int[lookAheadBufferSize]; currentPositionInInput = 0; initializeLookAheadBuffer(); } /** * To be implemented by concrete implementations * * @return the next token from the input. */ public abstract Token nextToken(); // PROTECTED protected int getCurrentPositionInInput() { return currentPositionInInput; } /* * Returns the character at the specified position in the lookahead buffer. */ protected char lookAhead(int position) { return (char)lookAheadBuffer[position - 1]; } /** * Consume 1 character from the input. */ protected void consume() { currentPositionInInput++; loadNextCharacterFromInput(); } // PRIVATE /** * Returns true if the end of the stream has been reached. */ private bool isEndOfInput(int i) { return (END_OF_INPUT == i); } /** * Initialize the look ahead buffer from the input. */ private void initializeLookAheadBuffer() { for (int i = 0; i < lookAheadBufferSize; i++) { // Mark th entire buffer as being end of input. lookAheadBuffer[i] = END_OF_INPUT; } for (int i = 0; i < lookAheadBufferSize; i++) { // Now fill the buffer (if possible) from the input. lookAheadBuffer[i] = readInput(); if (isEndOfInput(lookAheadBuffer[i])) { // The input is smaller than the buffer size break; } } } /** * Loads the next character into the lookahead buffer if the end of the * stream has not already been reached. */ private void loadNextCharacterFromInput() { bool eoiEncountered = false; for (int i = 0; i < lookAheadBufferSize - 1; i++) { lookAheadBuffer[i] = lookAheadBuffer[i + 1]; if (isEndOfInput(lookAheadBuffer[i])) { eoiEncountered = true; break; } } if (!eoiEncountered) { lookAheadBuffer[lookAheadBufferSize - 1] = readInput(); } } private int readInput() { int read = -1; try { read = input.Read(); } catch (IOException ioe) { throw new LexerException("IOException thrown reading input.", currentPositionInInput, ioe); } return read; } } } ================================================ FILE: aima-csharp/logic/common/LexerException.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.logic.common { /** * A runtime exception to be used to describe Lexer exceptions. In particular it * provides information to help in identifying where in the input character * sequence the exception occurred. * * @author Ciaran O'Reilly * */ public class LexerException : SystemException { private static readonly long serialVersionUID = 1L; private int currentPositionInInput; public LexerException(String message, int currentPositionInInput): base(message) { this.currentPositionInInput = currentPositionInInput; } public LexerException(String message, int currentPositionInInput, Exception cause): base(message, cause) { this.currentPositionInInput = currentPositionInInput; } /** * * @return the current position in the input character stream that the lexer * was at before the exception was encountered. */ public int getCurrentPositionInInputExceptionThrown() { return currentPositionInInput; } } } ================================================ FILE: aima-csharp/logic/common/LogicTokenTypes.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.logic.common { /** * @author Ravi Mohan * */ public enum LogicTokenTypes : int { SYMBOL = 1, LPAREN = 2, RPAREN = 3, COMMA = 4, CONNECTOR = 5, QUANTIFIER = 6, PREDICATE = 7, FUNCTION = 8, VARIABLE = 9, CONSTANT = 10, TRUE = 11, FALSE = 12, EQUALS = 13, WHITESPACE = 1000, EOI = 9999 // End of Input. } } ================================================ FILE: aima-csharp/logic/common/Parser.cs ================================================ using System; using System.Collections.Generic; using System.IO; namespace aima.core.logic.common { /** * An abstract base class for constructing parsers for knowledge representation * languages. It provides a mechanism for converting a sequence of tokens * (derived from an appropriate lexer) into a syntactically correct abstract * syntax tree of the representation language. * * @author Ravi Mohan * @author Ciaran O'Reilly * * @param the root type of the abstract syntax tree being parsed. */ public abstract class Parser { protected int lookAheadBufferSize = 1; private Token[] lookAheadBuffer = null; private StringReader input; /** * * @return an instance of the Lexer to be used by a concrete implementation * of this class. */ public abstract Lexer getLexer(); /** * Parse the input concrete syntax into an abstract syntax tree. * * @param input * a string representation of the concrete syntax to be parsed. * @return the root node of an abstract syntax tree representation of the * the concrete input syntax that was parsed. */ public S parse(String input) { return parse(new StringReader(input)); } /** * Parse the input concrete syntax into an abstract syntax tree. * * @param inputReader * a Reader of the concrete syntax to be parsed. * @return the root node of an abstract syntax tree representation of the * the concrete input syntax that was parsed. */ public S parse(StringReader inputReader) { S result; try { getLexer().setInput(inputReader); initializeLookAheadBuffer(); result = parse(); } catch (LexerException le) { throw new ParserException("Lexer Exception thrown during parsing at position " + le.getCurrentPositionInInputExceptionThrown(), le); } return default(S); } // PROTECTED /** * To be implemented by concrete implementations of this class. * * @return the root node of an abstract syntax tree representation of the * the concrete input syntax that was parsed. */ protected abstract S parse(); /** * @return the token at the specified position in the lookahead buffer. */ protected Token lookAhead(int i) { return lookAheadBuffer[i - 1]; } /** * Consume 1 token from the input. */ protected void consume() { loadNextTokenFromInput(); } /** * Consume the given match symbol if it matches the current input token. If * it does not match throws a ParserException detailing the match error. * * @param toMatchSymbol * the symbol to match before consuming it. */ protected void match(String toMatchSymbol) { if (lookAhead(1).getText().Equals(toMatchSymbol)) { consume(); } else { throw new ParserException( "Parser: Syntax error detected at match. Expected " + toMatchSymbol + " but got " + lookAhead(1).getText(), lookAhead(1)); } } // PRIVATE private void initializeLookAheadBuffer() { lookAheadBuffer = new Token[lookAheadBufferSize]; for (int i = 0; i < lookAheadBufferSize; i++) { // Now fill the buffer (if possible) from the input. lookAheadBuffer[i] = getLexer().nextToken(); if (isEndOfInput(lookAheadBuffer[i])) { // The input is smaller than the buffer size break; } } } /* * Loads the next token into the lookahead buffer if the end of the stream * has not already been reached. */ private void loadNextTokenFromInput() { bool eoiEncountered = false; for (int i = 0; i < lookAheadBufferSize - 1; i++) { lookAheadBuffer[i] = lookAheadBuffer[i + 1]; if (isEndOfInput(lookAheadBuffer[i])) { eoiEncountered = true; break; } } if (!eoiEncountered) { lookAheadBuffer[lookAheadBufferSize - 1] = getLexer().nextToken(); } } /* * Returns true if the end of the stream has been reached. */ private bool isEndOfInput(Token t) { return (t == null || EqualityComparer.Equals(t.getType(), LogicTokenTypes.EOI)); } } } ================================================ FILE: aima-csharp/logic/common/ParserException.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.logic.common { /** * A runtime exception to be used to describe Parser exceptions. In particular * it provides information to help in identifying which tokens proved * problematic in the parse. * * @author Ciaran O'Reilly * */ public class ParserException : SystemException { private static readonly long serialVersionUID = 1L; private List problematicTokens = new List(); public ParserException(String message, params Token[] problematicTokens): base(message) { if (problematicTokens != null) { foreach (Token pt in problematicTokens) { this.problematicTokens.Add(pt); } } } public ParserException(String message, Exception cause, params Token[] problematicTokens): base(message, cause) { if (problematicTokens != null) { foreach (Token pt in problematicTokens) { this.problematicTokens.Add(pt); } } } /** * * @return a list of 0 or more tokens from the input stream that are * believed to have contributed to the parse exception. */ public List getProblematicTokens() { return problematicTokens; } } } ================================================ FILE: aima-csharp/logic/common/ParserTreeNode.cs ================================================ namespace aima.core.logic.common { /** * @author Ravi Mohan * */ public interface ParseTreeNode { } } ================================================ FILE: aima-csharp/logic/common/Token.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.logic.common { /** * A token generated by a lexer from a sequence of characters. * * @author Ravi Mohan * @author Ciaran O'Reilly * @author Mike Stampone */ public class Token { private int type; private String text; private int startCharPositionInInput; /** * Constructs a token from the specified token-name and attribute-value * * @param type * the token-name * @param text * the attribute-value * @param startCharPositionInInput * the position (starting from 0) at which this token * starts in the input. */ public Token(int type, String text, int startCharPositionInInput) { this.type = type; this.text = text; this.startCharPositionInInput = startCharPositionInInput; } /** * Returns the attribute-value of this token. * * @return the attribute-value of this token. */ public String getText() { return text; } /** * Returns the token-name of this token. * * @return the token-name of this token. */ public int getType() { return type; } /** * @return the position (starting from 0) at which this token starts in the * input. */ public int getStartCharPositionInInput() { return startCharPositionInInput; } public override bool Equals(Object o) { if (this == o) { return true; } if ((o == null) || !(o is Token)) { return false; } Token other = (Token)o; return ((other.type == type) && (other.text.Equals(text)) && (other.startCharPositionInInput == startCharPositionInInput)); } public override int GetHashCode() { int result = 17; result = 37 * result + type; result = 37 * result + text.GetHashCode(); result = 37 * result + startCharPositionInInput; return result; } public override String ToString() { return "[ " + type + " " + text + " " + startCharPositionInInput + " ]"; } } } ================================================ FILE: aima-csharp/logic/fol/CNFConverter.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing; using aima.core.logic.fol.parsing.ast; using aima.core.util; namespace aima.core.logic.fol { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 345.
*
* Every sentence of first-order logic can be converted into an inferentially * equivalent CNF sentence.
*
* Note: Transformation rules extracted from 346 and 347, which are * essentially the INSEADO method outlined in: INSEADO Rules * * @author Ciaran O'Reilly * @author Mike Stampone */ public class CNFConverter { private FOLParser parser = null; private SubstVisitor substVisitor; public CNFConverter(FOLParser parser) { this.parser = parser; this.substVisitor = new SubstVisitor(); } /** * Returns the specified sentence as a list of clauses, where each clause is * a disjunction of literals. * * @param aSentence * a sentence in first order logic (predicate calculus) * * @return the specified sentence as a list of clauses, where each clause is * a disjunction of literals. */ public CNF convertToCNF(Sentence aSentence) { // I)mplications Out: Sentence implicationsOut = (Sentence)aSentence.accept( new ImplicationsOut(), null); // N)egations In: Sentence negationsIn = (Sentence)implicationsOut.accept( new NegationsIn(), null); // S)tandardize variables: // For sentences like: // (FORALL x P(x)) V (EXISTS x Q(x)), // which use the same variable name twice, change the name of one of the // variables. Sentence saQuantifiers = (Sentence)negationsIn.accept( new StandardizeQuantiferVariables(substVisitor), new LinkedHashSet()); // Remove explicit quantifiers, by skolemizing existentials // and dropping universals: // E)xistentials Out // A)lls Out: Sentence andsAndOrs = (Sentence)saQuantifiers.accept( new RemoveQuantifiers(parser), new LinkedHashSet()); // D)istribution // V over ^: Sentence orDistributedOverAnd = (Sentence)andsAndOrs.accept( new DistributeOrOverAnd(), null); // O)perators Out return (new CNFConstructor()).construct(orDistributedOverAnd); } } class ImplicationsOut : FOLVisitor { public ImplicationsOut() { } public Object visitPredicate(Predicate p, Object arg) { return p; } public Object visitTermEquality(TermEquality equality, Object arg) { return equality; } public Object visitVariable(Variable variable, Object arg) { return variable; } public Object visitConstant(Constant constant, Object arg) { return constant; } public Object visitFunction(Function function, Object arg) { return function; } public Object visitNotSentence(NotSentence notSentence, Object arg) { Sentence negated = notSentence.getNegated(); return new NotSentence((Sentence)negated.accept(this, arg)); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { Sentence alpha = (Sentence)sentence.getFirst().accept(this, arg); Sentence beta = (Sentence)sentence.getSecond().accept(this, arg); // Eliminate <=>, bi-conditional elimination, // replace (alpha <=> beta) with (~alpha V beta) ^ (alpha V ~beta). if (Connectors.isBICOND(sentence.getConnector())) { Sentence first = new ConnectedSentence(Connectors.OR, new NotSentence(alpha), beta); Sentence second = new ConnectedSentence(Connectors.OR, alpha, new NotSentence(beta)); return new ConnectedSentence(Connectors.AND, first, second); } // Eliminate =>, implication elimination, // replacing (alpha => beta) with (~alpha V beta) if (Connectors.isIMPLIES(sentence.getConnector())) { return new ConnectedSentence(Connectors.OR, new NotSentence(alpha), beta); } return new ConnectedSentence(sentence.getConnector(), alpha, beta); } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { return new QuantifiedSentence(sentence.getQuantifier(), sentence .getVariables(), (Sentence)sentence.getQuantified().accept( this, arg)); } } class NegationsIn : FOLVisitor { public NegationsIn() { } public Object visitPredicate(Predicate p, Object arg) { return p; } public Object visitTermEquality(TermEquality equality, Object arg) { return equality; } public Object visitVariable(Variable variable, Object arg) { return variable; } public Object visitConstant(Constant constant, Object arg) { return constant; } public Object visitFunction(Function function, Object arg) { return function; } public Object visitNotSentence(NotSentence notSentence, Object arg) { // CNF requires NOT (~) to appear only in literals, so we 'move ~ // inwards' by repeated application of the following equivalences: Sentence negated = notSentence.getNegated(); // ~(~alpha) equivalent to alpha (double negation elimination) if (negated is NotSentence) { return ((NotSentence)negated).getNegated().accept(this, arg); } if (negated is ConnectedSentence) { ConnectedSentence negConnected = (ConnectedSentence)negated; Sentence alpha = negConnected.getFirst(); Sentence beta = negConnected.getSecond(); // ~(alpha ^ beta) equivalent to (~alpha V ~beta) (De Morgan) if (Connectors.isAND(negConnected.getConnector())) { // I need to ensure the ~s are moved in deeper Sentence notAlpha = (Sentence)(new NotSentence(alpha)).accept( this, arg); Sentence notBeta = (Sentence)(new NotSentence(beta)).accept( this, arg); return new ConnectedSentence(Connectors.OR, notAlpha, notBeta); } // ~(alpha V beta) equivalent to (~alpha ^ ~beta) (De Morgan) if (Connectors.isOR(negConnected.getConnector())) { // I need to ensure the ~s are moved in deeper Sentence notAlpha = (Sentence)(new NotSentence(alpha)).accept( this, arg); Sentence notBeta = (Sentence)(new NotSentence(beta)).accept( this, arg); return new ConnectedSentence(Connectors.AND, notAlpha, notBeta); } } // in addition, rules for negated quantifiers: if (negated is QuantifiedSentence) { QuantifiedSentence negQuantified = (QuantifiedSentence)negated; // I need to ensure the ~ is moved in deeper Sentence notP = (Sentence)(new NotSentence(negQuantified .getQuantified())).accept(this, arg); // ~FORALL x p becomes EXISTS x ~p if (Quantifiers.isFORALL(negQuantified.getQuantifier())) { return new QuantifiedSentence(Quantifiers.EXISTS, negQuantified .getVariables(), notP); } // ~EXISTS x p becomes FORALL x ~p if (Quantifiers.isEXISTS(negQuantified.getQuantifier())) { return new QuantifiedSentence(Quantifiers.FORALL, negQuantified .getVariables(), notP); } } return new NotSentence((Sentence)negated.accept(this, arg)); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { return new ConnectedSentence(sentence.getConnector(), (Sentence)sentence.getFirst().accept(this, arg), (Sentence)sentence.getSecond().accept(this, arg)); } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { return new QuantifiedSentence(sentence.getQuantifier(), sentence .getVariables(), (Sentence)sentence.getQuantified().accept( this, arg)); } } class StandardizeQuantiferVariables : FOLVisitor { // Just use a localized indexical here. private ApartIndexical quantifiedIndexical = new ApartIndexical(); private class ApartIndexical : StandardizeApartIndexical { private int index = 0; public String getPrefix() { return "q"; } public int getNextIndex() { return index++; } } private SubstVisitor substVisitor = null; public StandardizeQuantiferVariables(SubstVisitor substVisitor) { this.substVisitor = substVisitor; } public Object visitPredicate(Predicate p, Object arg) { return p; } public Object visitTermEquality(TermEquality equality, Object arg) { return equality; } public Object visitVariable(Variable variable, Object arg) { return variable; } public Object visitConstant(Constant constant, Object arg) { return constant; } public Object visitFunction(Function function, Object arg) { return function; } public Object visitNotSentence(NotSentence sentence, Object arg) { return new NotSentence((Sentence)sentence.getNegated().accept(this, arg)); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { return new ConnectedSentence(sentence.getConnector(), (Sentence)sentence.getFirst().accept(this, arg), (Sentence)sentence.getSecond().accept(this, arg)); } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { List seenSoFar = (List)arg; // Keep track of what I have to subst locally and // what my renamed variables will be. Dictionary localSubst = new Dictionary(); List replVariables = new List(); foreach (Variable v in sentence.getVariables()) { // If local variable has be renamed already // then I need to come up with own name if (seenSoFar.Contains(v)) { Variable sV = new Variable(quantifiedIndexical.getPrefix() + quantifiedIndexical.getNextIndex()); localSubst.Add(v, sV); // Replacement variables should contain new name for variable replVariables.Add(sV); } else { // Not already replaced, this name is good replVariables.Add(v); } } // Apply the local subst Sentence subst = substVisitor.subst(localSubst, sentence .getQuantified()); // Ensure all my existing and replaced variable // names are tracked seenSoFar.AddRange(replVariables); Sentence sQuantified = (Sentence)subst.accept(this, arg); return new QuantifiedSentence(sentence.getQuantifier(), replVariables, sQuantified); } } class RemoveQuantifiers : FOLVisitor { private FOLParser parser = null; private SubstVisitor substVisitor = null; public RemoveQuantifiers(FOLParser parser) { this.parser = parser; substVisitor = new SubstVisitor(); } public Object visitPredicate(Predicate p, Object arg) { return p; } public Object visitTermEquality(TermEquality equality, Object arg) { return equality; } public Object visitVariable(Variable variable, Object arg) { return variable; } public Object visitConstant(Constant constant, Object arg) { return constant; } public Object visitFunction(Function function, Object arg) { return function; } public Object visitNotSentence(NotSentence sentence, Object arg) { return new NotSentence((Sentence)sentence.getNegated().accept(this, arg)); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { return new ConnectedSentence(sentence.getConnector(), (Sentence)sentence.getFirst().accept(this, arg), (Sentence)sentence.getSecond().accept(this, arg)); } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { Sentence quantified = sentence.getQuantified(); List universalScope = (List)arg; // Skolemize: Skolemization is the process of removing existential // quantifiers by elimination. This is done by introducing Skolem // functions. The general rule is that the arguments of the Skolem // function are all the universally quantified variables in whose // scope the existential quantifier appears. if (Quantifiers.isEXISTS(sentence.getQuantifier())) { Dictionary skolemSubst = new Dictionary(); foreach (Variable eVar in sentence.getVariables()) { if (universalScope.Count > 0) { // Replace with a Skolem Function String skolemFunctionName = parser.getFOLDomain() .addSkolemFunction(); skolemSubst.Add(eVar, new Function(skolemFunctionName, new List(universalScope))); } else { // Replace with a Skolem Constant String skolemConstantName = parser.getFOLDomain() .addSkolemConstant(); skolemSubst.Add(eVar, new Constant(skolemConstantName)); } } Sentence skolemized = substVisitor.subst(skolemSubst, quantified); return skolemized.accept(this, arg); } // Drop universal quantifiers. if (Quantifiers.isFORALL(sentence.getQuantifier())) { // Add to the universal scope so that // existential skolemization may be done correctly universalScope.AddRange(sentence.getVariables()); Sentence droppedUniversal = (Sentence)quantified.accept(this, arg); // Enusre my scope is removed before moving back up // the call stack when returning foreach (Variable s in sentence.getVariables()) { universalScope.Remove(s); } return droppedUniversal; } // Should not reach here as have already // handled the two quantifiers. throw new ApplicationException("Unhandled Quantifier:" + sentence.getQuantifier()); } } class DistributeOrOverAnd : FOLVisitor { public DistributeOrOverAnd() { } public Object visitPredicate(Predicate p, Object arg) { return p; } public Object visitTermEquality(TermEquality equality, Object arg) { return equality; } public Object visitVariable(Variable variable, Object arg) { return variable; } public Object visitConstant(Constant constant, Object arg) { return constant; } public Object visitFunction(Function function, Object arg) { return function; } public Object visitNotSentence(NotSentence sentence, Object arg) { return new NotSentence((Sentence)sentence.getNegated().accept(this, arg)); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { // Distribute V over ^: // This will cause flattening out of nested ^s and Vs Sentence alpha = (Sentence)sentence.getFirst().accept(this, arg); Sentence beta = (Sentence)sentence.getSecond().accept(this, arg); // (alpha V (beta ^ gamma)) equivalent to // ((alpha V beta) ^ (alpha V gamma)) if (Connectors.isOR(sentence.getConnector()) && beta is ConnectedSentence) { ConnectedSentence betaAndGamma = (ConnectedSentence)beta; if (Connectors.isAND(betaAndGamma.getConnector())) { beta = betaAndGamma.getFirst(); Sentence gamma = betaAndGamma.getSecond(); return new ConnectedSentence(Connectors.AND, (Sentence)(new ConnectedSentence(Connectors.OR, alpha, beta)).accept(this, arg), (Sentence)(new ConnectedSentence(Connectors.OR, alpha, gamma)).accept(this, arg)); } } // ((alpha ^ gamma) V beta) equivalent to // ((alpha V beta) ^ (gamma V beta)) if (Connectors.isOR(sentence.getConnector()) && alpha is ConnectedSentence) { ConnectedSentence alphaAndGamma = (ConnectedSentence)alpha; if (Connectors.isAND(alphaAndGamma.getConnector())) { alpha = alphaAndGamma.getFirst(); Sentence gamma = alphaAndGamma.getSecond(); return new ConnectedSentence(Connectors.AND, (Sentence)(new ConnectedSentence(Connectors.OR, alpha, beta)).accept(this, arg), (Sentence)(new ConnectedSentence(Connectors.OR, gamma, beta)).accept(this, arg)); } } return new ConnectedSentence(sentence.getConnector(), alpha, beta); } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { // This should not be called as should have already // removed all of the quantifiers. throw new NotImplementedException( "All quantified sentences should have already been removed."); } } class CNFConstructor : FOLVisitor { public CNFConstructor() { } public CNF construct(Sentence orDistributedOverAnd) { ArgData ad = new ArgData(); orDistributedOverAnd.accept(this, ad); return new CNF(ad.clauses); } public Object visitPredicate(Predicate p, Object arg) { ArgData ad = (ArgData)arg; if (ad.negated) { ad.clauses[ad.clauses.Count - 1].addNegativeLiteral(p); } else { ad.clauses[ad.clauses.Count - 1].addPositiveLiteral(p); } return p; } public Object visitTermEquality(TermEquality equality, Object arg) { ArgData ad = (ArgData)arg; if (ad.negated) { ad.clauses[ad.clauses.Count - 1].addNegativeLiteral(equality); } else { ad.clauses[ad.clauses.Count - 1].addPositiveLiteral(equality); } return equality; } public Object visitVariable(Variable variable, Object arg) { // This should not be called throw new NotImplementedException("visitVariable() should not be called."); } public Object visitConstant(Constant constant, Object arg) { // This should not be called throw new NotImplementedException("visitConstant() should not be called."); } public Object visitFunction(Function function, Object arg) { // This should not be called throw new NotImplementedException("visitFunction() should not be called."); } public Object visitNotSentence(NotSentence sentence, Object arg) { ArgData ad = (ArgData)arg; // Indicate that the enclosed predicate is negated ad.negated = true; sentence.getNegated().accept(this, arg); ad.negated = false; return sentence; } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { ArgData ad = (ArgData)arg; Sentence first = sentence.getFirst(); Sentence second = sentence.getSecond(); first.accept(this, arg); if (Connectors.isAND(sentence.getConnector())) { ad.clauses.Add(new Clause()); } second.accept(this, arg); return sentence; } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { // This should not be called as should have already // removed all of the quantifiers. throw new NotImplementedException( "All quantified sentences should have already been removed."); } class ArgData { public List clauses = new List(); public bool negated = false; public ArgData() { clauses.Add(new Clause()); } } } } ================================================ FILE: aima-csharp/logic/fol/Connectors.cs ================================================ using System; namespace aima.core.logic.fol { /** * @author Ravi Mohan * */ public class Connectors { public static readonly String AND = "AND"; public static readonly String OR = "OR"; public static readonly String NOT = "NOT"; public static readonly String IMPLIES = "=>"; public static readonly String BICOND = "<=>"; public static bool isAND(String connector) { return AND.Equals(connector); } public static bool isOR(String connector) { return OR.Equals(connector); } public static bool isNOT(String connector) { return NOT.Equals(connector); } public static bool isIMPLIES(String connector) { return IMPLIES.Equals(connector); } public static bool isBICOND(String connector) { return BICOND.Equals(connector); } } } ================================================ FILE: aima-csharp/logic/fol/PredicateCollector.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol { /** * @author Ravi Mohan * */ public class PredicateCollector : FOLVisitor { public PredicateCollector() { } public List getPredicates(Sentence s) { return (List)s.accept(this, new List()); } public Object visitPredicate(Predicate p, Object arg) { List predicates = (List)arg; predicates.Add(p); return predicates; } public Object visitTermEquality(TermEquality equality, Object arg) { return arg; } public Object visitVariable(Variable variable, Object arg) { return arg; } public Object visitConstant(Constant constant, Object arg) { return arg; } public Object visitFunction(Function function, Object arg) { return arg; } public Object visitNotSentence(NotSentence sentence, Object arg) { sentence.getNegated().accept(this, arg); return arg; } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { sentence.getFirst().accept(this, arg); sentence.getSecond().accept(this, arg); return arg; } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { sentence.getQuantified().accept(this, arg); return arg; } } } ================================================ FILE: aima-csharp/logic/fol/Quantifiers.cs ================================================ using System; namespace aima.core.logic.fol { /** * @author Ciaran O'Reilly * */ public class Quantifiers { public static readonly String FORALL = "FORALL"; public static readonly String EXISTS = "EXISTS"; public static bool isFORALL(String quantifier) { return FORALL.Equals(quantifier); } public static bool isEXISTS(String quantifier) { return EXISTS.Equals(quantifier); } } } ================================================ FILE: aima-csharp/logic/fol/StandardizeApart.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.inference.proof; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol { /** * @author Ciaran O'Reilly * */ public class StandardizeApart { private VariableCollector variableCollector = null; private SubstVisitor substVisitor = null; public StandardizeApart() { variableCollector = new VariableCollector(); substVisitor = new SubstVisitor(); } public StandardizeApart(VariableCollector variableCollector, SubstVisitor substVisitor) { this.variableCollector = variableCollector; this.substVisitor = substVisitor; } // Note: see page 327. public StandardizeApartResult standardizeApart(Sentence sentence, StandardizeApartIndexical standardizeApartIndexical) { List toRename = variableCollector.collectAllVariables(sentence); Dictionary renameSubstitution = new Dictionary(); Dictionary reverseSubstitution = new Dictionary(); foreach (Variable var in toRename) { Variable v = null; do { v = new Variable(standardizeApartIndexical.getPrefix() + standardizeApartIndexical.getNextIndex()); // Ensure the new variable name is not already // accidentally used in the sentence } while (toRename.Contains(v)); renameSubstitution.Add(var, v); reverseSubstitution.Add(v, var); } Sentence standardized = substVisitor.subst(renameSubstitution, sentence); return new StandardizeApartResult(sentence, standardized, renameSubstitution, reverseSubstitution); } public Clause standardizeApart(Clause clause, StandardizeApartIndexical standardizeApartIndexical) { List toRename = variableCollector.collectAllVariables(clause); Dictionary renameSubstitution = new Dictionary(); foreach (Variable var in toRename) { Variable v = null; do { v = new Variable(standardizeApartIndexical.getPrefix() + standardizeApartIndexical.getNextIndex()); // Ensure the new variable name is not already // accidentally used in the sentence } while (toRename.Contains(v)); renameSubstitution.Add(var, v); } if (renameSubstitution.Count > 0) { List literals = new List(); foreach (Literal l in clause.getLiterals()) { literals.Add(substVisitor.subst(renameSubstitution, l)); } Clause renamed = new Clause(literals); renamed.setProofStep(new ProofStepRenaming(renamed, clause .getProofStep())); return renamed; } return clause; } public Chain standardizeApart(Chain chain, StandardizeApartIndexical standardizeApartIndexical) { List toRename = variableCollector.collectAllVariables(chain); Dictionary renameSubstitution = new Dictionary(); foreach (Variable var in toRename) { Variable v = null; do { v = new Variable(standardizeApartIndexical.getPrefix() + standardizeApartIndexical.getNextIndex()); // Ensure the new variable name is not already // accidentally used in the sentence } while (toRename.Contains(v)); renameSubstitution.Add(var, v); } if (renameSubstitution.Count > 0) { List lits = new List(); foreach (Literal l in chain.getLiterals()) { AtomicSentence atom = (AtomicSentence)substVisitor.subst( renameSubstitution, l.getAtomicSentence()); lits.Add(l.newInstance(atom)); } Chain renamed = new Chain(lits); renamed.setProofStep(new ProofStepRenaming(renamed, chain .getProofStep())); return renamed; } return chain; } public Dictionary standardizeApart(List l1Literals, List l2Literals, StandardizeApartIndexical standardizeApartIndexical) { List toRename = new List(); foreach (Literal pl in l1Literals) { toRename.AddRange(variableCollector.collectAllVariables(pl .getAtomicSentence())); } foreach (Literal nl in l2Literals) { toRename.AddRange(variableCollector.collectAllVariables(nl .getAtomicSentence())); } Dictionary renameSubstitution = new Dictionary(); foreach (Variable var in toRename) { Variable v = null; do { v = new Variable(standardizeApartIndexical.getPrefix() + standardizeApartIndexical.getNextIndex()); // Ensure the new variable name is not already // accidentally used in the sentence } while (toRename.Contains(v)); renameSubstitution.Add(var, v); } List posLits = new List(); List negLits = new List(); foreach (Literal pl in l1Literals) { posLits.Add(substVisitor.subst(renameSubstitution, pl)); } foreach (Literal nl in l2Literals) { negLits.Add(substVisitor.subst(renameSubstitution, nl)); } l1Literals.Clear(); l1Literals.AddRange(posLits); l2Literals.Clear(); l2Literals.AddRange(negLits); return renameSubstitution; } } } ================================================ FILE: aima-csharp/logic/fol/StandardizeApartInPlace.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol { /** * @author Ciaran O'Reilly * */ public class StandardizeApartInPlace { private static CollectAllVariables _collectAllVariables = new CollectAllVariables(); public static int standardizeApart(Chain c, int saIdx) { List variables = new List(); foreach (Literal l in c.getLiterals()) { collectAllVariables(l.getAtomicSentence(), variables); } return standardizeApart(variables, c, saIdx); } public static int standardizeApart(Clause c, int saIdx) { List variables = new List(); foreach (Literal l in c.getLiterals()) { collectAllVariables(l.getAtomicSentence(), variables); } return standardizeApart(variables, c, saIdx); } // PRIVATE METHODS private static int standardizeApart(List variables, Object expr, int saIdx) { Dictionary indexicals = new Dictionary(); foreach (Variable v in variables) { if (!indexicals.ContainsKey(v.getIndexedValue())) { indexicals.Add(v.getIndexedValue(), saIdx++); } } foreach (Variable v in variables) { int i = indexicals[v.getIndexedValue()]; if (null == i) { throw new ApplicationException("ERROR: duplicate var=" + v + ", expr=" + expr); } else { v.setIndexical(i); } } return saIdx; } private static void collectAllVariables(Sentence s, List vars) { s.accept(_collectAllVariables, vars); } } class CollectAllVariables : FOLVisitor { public CollectAllVariables() { } public Object visitVariable(Variable var, Object arg) { List variables = (List)arg; variables.Add(var); return var; } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { // Ensure I collect quantified variables too List variables = (List)arg; variables.AddRange(sentence.getVariables()); sentence.getQuantified().accept(this, arg); return sentence; } public Object visitPredicate(Predicate predicate, Object arg) { foreach (Term t in predicate.getTerms()) { t.accept(this, arg); } return predicate; } public Object visitTermEquality(TermEquality equality, Object arg) { equality.getTerm1().accept(this, arg); equality.getTerm2().accept(this, arg); return equality; } public Object visitConstant(Constant constant, Object arg) { return constant; } public Object visitFunction(Function function, Object arg) { foreach (Term t in function.getTerms()) { t.accept(this, arg); } return function; } public Object visitNotSentence(NotSentence sentence, Object arg) { sentence.getNegated().accept(this, arg); return sentence; } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { sentence.getFirst().accept(this, arg); sentence.getSecond().accept(this, arg); return sentence; } } } ================================================ FILE: aima-csharp/logic/fol/StandardizeApartIndexical.cs ================================================ using System; namespace aima.core.logic.fol { /** * @author Ciaran O'Reilly * */ public interface StandardizeApartIndexical { String getPrefix(); int getNextIndex(); } } ================================================ FILE: aima-csharp/logic/fol/StandardizeApartIndexicalFactory.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace aima.core.logic.fol { /** * This class ensures unique standardize apart indexicals are created. * * @author Ciaran O'Reilly * */ public class StandardizeApartIndexicalFactory { private static Dictionary _assignedIndexicals = new Dictionary(); // For use in test cases, where predictable behavior is expected. public static void flush() { lock (_assignedIndexicals) { _assignedIndexicals.Clear(); } } public static StandardizeApartIndexical newStandardizeApartIndexical( Char preferredPrefix) { char ch = preferredPrefix; if (!(Char.IsLetter(ch) && Char.IsLower(ch))) { throw new ArgumentException("Preferred prefix :" + preferredPrefix + " must be a valid a lower case letter."); } StringBuilder sb = new StringBuilder(); lock (_assignedIndexicals) { int currentPrefixCnt = -1; if (!_assignedIndexicals.ContainsKey(preferredPrefix)) { currentPrefixCnt = 0; _assignedIndexicals.Add(preferredPrefix, currentPrefixCnt); } else { currentPrefixCnt += 1; _assignedIndexicals[preferredPrefix] = currentPrefixCnt; } sb.Append(preferredPrefix); for (int i = 0; i < currentPrefixCnt; i++) { sb.Append(preferredPrefix); } } return new StandardizeApartIndexicalImpl(sb.ToString()); } } class StandardizeApartIndexicalImpl : StandardizeApartIndexical { private String prefix = null; private int index = 0; public StandardizeApartIndexicalImpl(String prefix) { this.prefix = prefix; } // START-StandardizeApartIndexical public String getPrefix() { return prefix; } public int getNextIndex() { return index++; } // END-StandardizeApartIndexical } } ================================================ FILE: aima-csharp/logic/fol/StandardizeApartResult.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol { /** * @author Ciaran O'Reilly * */ public class StandardizeApartResult { private Sentence originalSentence = null; private Sentence standardized = null; private Dictionary forwardSubstitution = null; private Dictionary reverseSubstitution = null; public StandardizeApartResult(Sentence originalSentence, Sentence standardized, Dictionary forwardSubstitution, Dictionary reverseSubstitution) { this.originalSentence = originalSentence; this.standardized = standardized; this.forwardSubstitution = forwardSubstitution; this.reverseSubstitution = reverseSubstitution; } public Sentence getOriginalSentence() { return originalSentence; } public Sentence getStandardized() { return standardized; } public Dictionary getForwardSubstitution() { return forwardSubstitution; } public Dictionary getReverseSubstitution() { return reverseSubstitution; } } } ================================================ FILE: aima-csharp/logic/fol/SubstVisitor.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class SubstVisitor : AbstractFOLVisitor { public SubstVisitor() { } /** * Note: Refer to Artificial Intelligence A Modern Approach (3rd Edition): * page 323. * * @param theta * a substitution. * @param sentence * the substitution has been applied to. * @return a new Sentence representing the result of applying the * substitution theta to aSentence. * */ public Sentence subst(Dictionary theta, Sentence sentence) { return (Sentence)sentence.accept(this, theta); } public Term subst(Dictionary theta, Term aTerm) { return (Term)aTerm.accept(this, theta); } public Function subst(Dictionary theta, Function function) { return (Function)function.accept(this, theta); } public Literal subst(Dictionary theta, Literal literal) { return literal.newInstance((AtomicSentence)literal .getAtomicSentence().accept(this, theta)); } public override Object visitVariable(Variable variable, Object arg) { Dictionary substitution = (Dictionary)arg; if (substitution.ContainsKey(variable)) { return substitution[variable].copy(); } return variable.copy(); } public override Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { Dictionary substitution = (Dictionary)arg; Sentence quantified = sentence.getQuantified(); Sentence quantifiedAfterSubs = (Sentence)quantified.accept(this, arg); List variables = new List(); foreach (Variable v in sentence.getVariables()) { if (substitution.ContainsKey(v)) { Term st = substitution[v]; if (st is Variable) { // Only if it is a variable to I replace it, otherwise // I drop it. variables.Add((Variable)st.copy()); } } else { // No substitution for the quantified variable, so // keep it. variables.Add((Variable)v.copy()); } } // If not variables remaining on the quantifier, then drop it if (variables.Count == 0) { return quantifiedAfterSubs; } return new QuantifiedSentence(sentence.getQuantifier(), variables, quantifiedAfterSubs); } } } ================================================ FILE: aima-csharp/logic/fol/SubsumptionElimination.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 356.
*
* The subsumption method eliminates all sentences that are subsumed by (that * is, more specific than) an existing sentence in the KB. For example, P(x) is * in the KB, then there is no sense in adding P(A) and even less sense in * adding P(A) V Q(B). Subsumption helps keep the KB small and thus helps keep * the search space small.
*
* Note: From slide 17.
*
* Relational Subsumption
*
* A relational clause Φ subsumes Ψ if and only if there is a * substitution δ that, when applied to Φ, produces a clause Φ' * that is a subset of Ψ. * * @author Ciaran O'Reilly * @author Mike Stampone */ public class SubsumptionElimination { /** * Returns the clauses that are subsumed by (that is, more specific than) an * existing clause in the specified set of clauses. * * @param clauses * a set of clauses in first order logic * * @return the clauses that are subsumed by (that is, more specific than) an * existing clause in the specified set of clauses. */ public static List findSubsumedClauses(List clauses) { List subsumed = new List(); // Group the clauses by their # of literals. // Keep track of the min and max # of literals. int min = int.MaxValue; int max = 0; Dictionary> clausesGroupedBySize = new Dictionary>(); foreach (Clause c in clauses) { int size = c.getNumberLiterals(); if (size < min) { min = size; } if (size > max) { max = size; } List cforsize = null; if (clausesGroupedBySize.ContainsKey(size)) { cforsize = clausesGroupedBySize[size]; } if (null == cforsize) { cforsize = new List(); clausesGroupedBySize.Add(size, cforsize); } cforsize.Add(c); } // Check if each smaller clause // subsumes any of the larger clauses. for (int i = min; i < max; i++) { List scs = clausesGroupedBySize[i]; // Ensure there are clauses with this # of literals if (null != scs) { for (int j = i + 1; j <= max; j++) { // Ensure there are clauses with this # of literals if (clausesGroupedBySize.ContainsKey(j)) { List lcs = clausesGroupedBySize[j]; foreach (Clause sc in scs) { // Don't bother checking clauses // that are already subsumed. if (!subsumed.Contains(sc)) { foreach (Clause lc in lcs) { if (!subsumed.Contains(lc)) { if (sc.subsumes(lc)) { subsumed.Add(lc); } } } } } } } } } return subsumed; } } } ================================================ FILE: aima-csharp/logic/fol/Unifier.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 9.1, page * 328.
*
* *
     * function UNIFY(x, y, theta) returns a substitution to make x and y identical
     *   inputs: x, a variable, constant, list, or compound
     *           y, a variable, constant, list, or compound
     *           theta, the substitution built up so far (optional, defaults to empty)
     *           
     *   if theta = failure then return failure
     *   else if x = y the return theta
     *   else if VARIABLE?(x) then return UNIVY-VAR(x, y, theta)
     *   else if VARIABLE?(y) then return UNIFY-VAR(y, x, theta)
     *   else if COMPOUND?(x) and COMPOUND?(y) then
     *       return UNIFY(x.ARGS, y.ARGS, UNIFY(x.OP, y.OP, theta))
     *   else if LIST?(x) and LIST?(y) then
     *       return UNIFY(x.REST, y.REST, UNIFY(x.FIRST, y.FIRST, theta))
     *   else return failure
     *   
     * ---------------------------------------------------------------------------------------------------
     * 
     * function UNIFY-VAR(var, x, theta) returns a substitution
     *            
     *   if {var/val} E theta then return UNIFY(val, x, theta)
     *   else if {x/val} E theta then return UNIFY(var, val, theta)
     *   else if OCCUR-CHECK?(var, x) then return failure
     *   else return add {var/x} to theta
     * 
* * Figure 9.1 The unification algorithm. The algorithm works by comparing the * structures of the inputs, elements by element. The substitution theta that is * the argument to UNIFY is built up along the way and is used to make sure that * later comparisons are consistent with bindings that were established earlier. * In a compound expression, such as F(A, B), the OP field picks out the * function symbol F and the ARGS field picks out the argument list (A, B). * * @author Ciaran O'Reilly * @author Ravi Mohan * @author Mike Stampone * */ public class Unifier { private static SubstVisitor _substVisitor = new SubstVisitor(); private VariableCollector _variableCollector; public Unifier() { } /** * Returns a Dictionary representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a failure to * unify. * * @param x * a variable, constant, list, or compound * @param y * a variable, constant, list, or compound * * @return a Dictionary representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a * failure to unify. */ public Dictionary unify(FOLNode x, FOLNode y) { return unify(x, y, new Dictionary()); } /** * Returns a Dictionary representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a failure to * unify. * * @param x * a variable, constant, list, or compound * @param y * a variable, constant, list, or compound * @param theta * the substitution built up so far * * @return a Dictionary representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a * failure to unify. */ public Dictionary unify(FOLNode x, FOLNode y, Dictionary theta) { // if theta = failure then return failure if (theta == null) { return null; } else if (x.Equals(y)) { // else if x = y then return theta return theta; } else if (x is Variable) { // else if VARIABLE?(x) then return UNIVY-VAR(x, y, theta) return unifyVar((Variable)x, y, theta); } else if (y is Variable) { // else if VARIABLE?(y) then return UNIFY-VAR(y, x, theta) return unifyVar((Variable)y, x, theta); } else if (isCompound(x) && isCompound(y)) { // else if COMPOUND?(x) and COMPOUND?(y) then // return UNIFY(x.ARGS, y.ARGS, UNIFY(x.OP, y.OP, theta)) return unify(args(x), args(y), unifyOps(op(x), op(y), theta)); } else { // else return failure return null; } } /** * Returns a Dictionary representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a failure to * unify. * * @param x * a variable, constant, list, or compound * @param y * a variable, constant, list, or compound * @param theta * the substitution built up so far * * @return a Dictionary representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a * failure to unify. */ // else if LIST?(x) and LIST?(y) then // return UNIFY(x.REST, y.REST, UNIFY(x.FIRST, y.FIRST, theta)) public Dictionary unify(List x, List y, Dictionary theta) { if (theta == null) { return null; } else if (x.Count != y.Count) { return null; } else if (x.Count == 0 && y.Count == 0) { return theta; } else if (x.Count == 1 && y.Count == 1) { return unify(x[0], y[0], theta); } else { return unify(x.Skip(1).ToList(), y.Skip(1).ToList(), unify( x[0], y[0], theta)); } } // PROTECTED METHODS // Note: You can subclass and override this method in order // to re-implement the OCCUR-CHECK?() to always // return false if you want that to be the default // behavior, as is the case with Prolog. protected bool occurCheck(Dictionary theta, Variable var, FOLNode x) { if (x is Function) { List varsToCheck = _variableCollector .collectAllVariables((Function)x); if (varsToCheck.Contains(var)) { return true; } // Now need to check if cascading will cause occurs to happen // e.g. // Loves(SF1(v2),v2) // Loves(v3,SF0(v3)) // or // P(v1,SF0(v1),SF0(v1)) // P(v2,SF0(v2),v2 ) // or // P(v1, F(v2),F(v2),F(v2),v1, F(F(v1)),F(F(F(v1))),v2) // P(F(v3),v4, v5, v6, F(F(v5)),v4, F(v3), F(F(v5))) return cascadeOccurCheck(theta, var, varsToCheck, new List(varsToCheck)); } return false; } // PRIVATE METHODS /** * * function UNIFY-VAR(var, x, theta) returns a substitution * inputs: var, a variable * x, any expression * theta, the substitution built up so far * */ private Dictionary unifyVar(Variable var, FOLNode x, Dictionary theta) { if (!(x is Term)) { return null; } else if (theta.ContainsKey(var)) { // if {var/val} E theta then return UNIFY(val, x, theta) return unify(theta[var], x, theta); } else if (theta.Keys.Contains(x)) { // else if {x/val} E theta then return UNIFY(var, val, theta) return unify(var, (FOLNode)theta[(Variable)x], theta); } else if (occurCheck(theta, var, x)) { // else if OCCUR-CHECK?(var, x) then return failure return null; } else { // else return add {var/x} to theta cascadeSubstitution(theta, var, (Term)x); return theta; } } private Dictionary unifyOps(String x, String y, Dictionary theta) { if (theta == null) { return null; } else if (x.Equals(y)) { return theta; } else { return null; } } private List args(FOLNode x) { return x.getArgs(); } private String op(FOLNode x) { return x.getSymbolicName(); } private bool isCompound(FOLNode x) { return x.isCompound(); } private bool cascadeOccurCheck(Dictionary theta, Variable var, List varsToCheck, List varsCheckedAlready) { // Want to check if any of the variable to check end up // looping back around on the new variable. List nextLevelToCheck = new List(); foreach (Variable v in varsToCheck) { Term t = null; if (theta.ContainsKey(v)) { t = theta[v]; } if (null == t) { // Variable may not be a key so skip continue; } if (t.Equals(var)) { // e.g. // v1=v2 // v2=SFO(v1) return true; } else if (t is Function) { // Need to ensure the function this variable // is to be replaced by does not contain var. List indirectvars = _variableCollector .collectAllVariables(t); if (indirectvars.Contains(var)) { return true; } else { // Determine the next cascade/level // of variables to check for looping foreach (Variable iv in indirectvars) { if (!varsCheckedAlready.Contains(iv)) { nextLevelToCheck.Add(iv); } } } } } if (nextLevelToCheck.Count > 0) { varsCheckedAlready.AddRange(nextLevelToCheck); return cascadeOccurCheck(theta, var, nextLevelToCheck, varsCheckedAlready); } return false; } // See: // http://logic.stanford.edu/classes/cs157/2008/miscellaneous/faq.html#jump165 // for need for this. private void cascadeSubstitution(Dictionary theta, Variable var, Term x) { theta.Add(var, x); List thetaKeys = theta.Keys.ToList(); foreach (Variable v in thetaKeys) { Term t = theta[v]; if (theta.ContainsKey(v)) { theta[v] = _substVisitor.subst(theta, t); } else { theta.Add(v, _substVisitor.subst(theta, t)); } } } } } ================================================ FILE: aima-csharp/logic/fol/VariableCollector.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing; using aima.core.logic.fol.parsing.ast; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class VariableCollector : FOLVisitor { public VariableCollector() { } // Note: The set guarantees the order in which they were // found. public List collectAllVariables(Sentence sentence) { List variables = new List(); sentence.accept(this, variables); return variables; } public List collectAllVariables(Term aTerm) { List variables = new List(); aTerm.accept(this, variables); return variables; } public List collectAllVariables(Clause aClause) { List variables = new List(); foreach (Literal l in aClause.getLiterals()) { l.getAtomicSentence().accept(this, variables); } return variables; } public List collectAllVariables(Chain aChain) { List variables = new List(); foreach (Literal l in aChain.getLiterals()) { l.getAtomicSentence().accept(this, variables); } return variables; } public Object visitVariable(Variable var, Object arg) { List variables = (List)arg; variables.Add(var); return var; } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { // Ensure I collect quantified variables too List variables = (List)arg; variables.AddRange(sentence.getVariables()); sentence.getQuantified().accept(this, arg); return sentence; } public Object visitPredicate(Predicate predicate, Object arg) { foreach (Term t in predicate.getTerms()) { t.accept(this, arg); } return predicate; } public Object visitTermEquality(TermEquality equality, Object arg) { equality.getTerm1().accept(this, arg); equality.getTerm2().accept(this, arg); return equality; } public Object visitConstant(Constant constant, Object arg) { return constant; } public Object visitFunction(Function function, Object arg) { foreach (Term t in function.getTerms()) { t.accept(this, arg); } return function; } public Object visitNotSentence(NotSentence sentence, Object arg) { sentence.getNegated().accept(this, arg); return sentence; } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { sentence.getFirst().accept(this, arg); sentence.getSecond().accept(this, arg); return sentence; } } } ================================================ FILE: aima-csharp/logic/fol/domain/DomainFactory.cs ================================================ using System; namespace aima.core.logic.fol.domain { /** * @author Ravi Mohan * */ public class DomainFactory { public static FOLDomain crusadesDomain() { FOLDomain domain = new FOLDomain(); domain.addConstant("John"); domain.addConstant("Richard"); domain.addConstant("England"); domain.addConstant("Saladin"); domain.addConstant("Crown"); domain.addFunction("LeftLegOf"); domain.addFunction("BrotherOf"); domain.addFunction("EnemyOf"); domain.addFunction("LegsOf"); domain.addPredicate("King"); return domain; } public static FOLDomain knowsDomain() { FOLDomain domain = new FOLDomain(); domain.addConstant("John"); domain.addConstant("Jane"); domain.addConstant("Bill"); domain.addConstant("Elizabeth"); domain.addFunction("Mother"); domain.addPredicate("Knows"); return domain; } public static FOLDomain weaponsDomain() { FOLDomain domain = new FOLDomain(); domain.addConstant("West"); domain.addConstant("America"); domain.addConstant("M1"); domain.addConstant("Nono"); domain.addPredicate("American"); domain.addPredicate("Weapon"); domain.addPredicate("Sells"); domain.addPredicate("Hostile"); domain.addPredicate("Criminal"); domain.addPredicate("Missile"); domain.addPredicate("Owns"); domain.addPredicate("Enemy"); return domain; } public static FOLDomain kingsDomain() { FOLDomain domain = new FOLDomain(); domain.addConstant("John"); domain.addConstant("Richard"); domain.addPredicate("King"); domain.addPredicate("Greedy"); domain.addPredicate("Evil"); return domain; } public static FOLDomain lovesAnimalDomain() { FOLDomain domain = new FOLDomain(); domain.addPredicate("Animal"); domain.addPredicate("Loves"); domain.addPredicate("Kills"); domain.addPredicate("Cat"); domain.addConstant("Jack"); domain.addConstant("Tuna"); domain.addConstant("Curiosity"); return domain; } public static FOLDomain ringOfThievesDomain() { FOLDomain domain = new FOLDomain(); domain.addPredicate("Parent"); domain.addPredicate("Caught"); domain.addPredicate("Friend"); domain.addPredicate("Skis"); domain.addConstant("Mike"); domain.addConstant("Joe"); domain.addConstant("Janet"); domain.addConstant("Nancy"); domain.addConstant("Ernie"); domain.addConstant("Bert"); domain.addConstant("Red"); domain.addConstant("Drew"); return domain; } } } ================================================ FILE: aima-csharp/logic/fol/domain/FOLDomain.cs ================================================ using System; using System.Collections.Generic; using aima.core.util; namespace aima.core.logic.fol.domain { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class FOLDomain { private HashSet constants, functions, predicates; private int skolemConstantIndexical = 0; private int skolemFunctionIndexical = 0; private int answerLiteralIndexical = 0; private List listeners = new List(); public FOLDomain() { this.constants = new HashSet(); this.functions = new HashSet(); this.predicates = new HashSet(); } public FOLDomain(FOLDomain toCopy): this(toCopy.getConstants(), toCopy.getFunctions(), toCopy .getPredicates()) { } public FOLDomain(HashSet constants, HashSet functions, HashSet predicates) { this.constants = new HashSet(constants); this.functions = new HashSet(functions); this.predicates = new HashSet(predicates); } public HashSet getConstants() { return constants; } public HashSet getFunctions() { return functions; } public HashSet getPredicates() { return predicates; } public void addConstant(String constant) { constants.Add(constant); } public String addSkolemConstant() { String sc = null; do { sc = "SC" + (skolemConstantIndexical++); } while (constants.Contains(sc) || functions.Contains(sc) || predicates.Contains(sc)); addConstant(sc); notifyFOLDomainListeners(new FOLDomainSkolemConstantAddedEvent(this, sc)); return sc; } public void addFunction(String function) { functions.Add(function); } public String addSkolemFunction() { String sf = null; do { sf = "SF" + (skolemFunctionIndexical++); } while (constants.Contains(sf) || functions.Contains(sf) || predicates.Contains(sf)); addFunction(sf); notifyFOLDomainListeners(new FOLDomainSkolemFunctionAddedEvent(this, sf)); return sf; } public void addPredicate(String predicate) { predicates.Add(predicate); } public String addAnswerLiteral() { String al = null; do { al = "Answer" + (answerLiteralIndexical++); } while (constants.Contains(al) || functions.Contains(al) || predicates.Contains(al)); addPredicate(al); notifyFOLDomainListeners(new FOLDomainAnswerLiteralAddedEvent(this, al)); return al; } public void addFOLDomainListener(FOLDomainListener listener) { lock (listeners) { if (!listeners.Contains(listener)) { listeners.Add(listener); } } } public void removeFOLDomainListener(FOLDomainListener listener) { lock (listeners) { listeners.Remove(listener); } } // PRIVATE METHODS private void notifyFOLDomainListeners(FOLDomainEvent evt) { lock (listeners) { foreach (FOLDomainListener l in listeners) { evt.notifyListener(l); } } } } } ================================================ FILE: aima-csharp/logic/fol/domain/FOLDomainAnswerLiteralAddedEvent.cs ================================================ using System; namespace aima.core.logic.fol.domain { /** * @author Ciaran O'Reilly * */ public class FOLDomainAnswerLiteralAddedEvent : FOLDomainEvent { private static readonly long serialVersionUID = 1L; private String answerLiteralName; public FOLDomainAnswerLiteralAddedEvent(Object source, String answerLiteralName): base(source) { this.answerLiteralName = answerLiteralName; } public String getAnswerLiteralNameName() { return answerLiteralName; } public override void notifyListener(FOLDomainListener listener) { listener.answerLiteralNameAdded(this); } } } ================================================ FILE: aima-csharp/logic/fol/domain/FOLDomainEvent.cs ================================================ using System; namespace aima.core.logic.fol.domain { /** * @author Ciaran O'Reilly * */ public abstract class FOLDomainEvent { private static readonly long serialVersionUID = 1L; public FOLDomainEvent(Object source) { } public abstract void notifyListener(FOLDomainListener listener); } } ================================================ FILE: aima-csharp/logic/fol/domain/FOLDomainListener.cs ================================================ using System; namespace aima.core.logic.fol.domain { /** * @author Ciaran O'Reilly * */ public interface FOLDomainListener { void skolemConstantAdded(FOLDomainSkolemConstantAddedEvent _event); void skolemFunctionAdded(FOLDomainSkolemFunctionAddedEvent _event); void answerLiteralNameAdded(FOLDomainAnswerLiteralAddedEvent _event); } } ================================================ FILE: aima-csharp/logic/fol/domain/FOLDomainSkolemConstantAddedEvent.cs ================================================ using System; namespace aima.core.logic.fol.domain { /** * @author Ciaran O'Reilly * */ public class FOLDomainSkolemConstantAddedEvent : FOLDomainEvent { private static readonly long serialVersionUID = 1L; private String skolemConstantName; public FOLDomainSkolemConstantAddedEvent(Object source, String skolemConstantName): base(source) { this.skolemConstantName = skolemConstantName; } public String getSkolemConstantName() { return skolemConstantName; } public override void notifyListener(FOLDomainListener listener) { listener.skolemConstantAdded(this); } } } ================================================ FILE: aima-csharp/logic/fol/domain/FOLDomainSkolemFunctionAddedEvent.cs ================================================ using System; namespace aima.core.logic.fol.domain { /** * @author Ciaran O'Reilly * */ public class FOLDomainSkolemFunctionAddedEvent : FOLDomainEvent { private static readonly long serialVersionUID = 1L; private String skolemFunctionName; public FOLDomainSkolemFunctionAddedEvent(Object source, String skolemFunctionName): base(source) { this.skolemFunctionName = skolemFunctionName; } public String getSkolemConstantName() { return skolemFunctionName; } public override void notifyListener(FOLDomainListener listener) { listener.skolemFunctionAdded(this); } } } ================================================ FILE: aima-csharp/logic/fol/inference/AbstractModulation.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol; using aima.core.logic.fol.parsing; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference { /** * Abstract base class for Demodulation and Paramodulation algorithms. * * @author Ciaran O'Reilly * */ public abstract class AbstractModulation { // PROTECTED ATTRIBUTES protected VariableCollector variableCollector = new VariableCollector(); protected Unifier unifier = new Unifier(); protected SubstVisitor substVisitor = new SubstVisitor(); // PROTECTED METODS public abstract bool isValidMatch(Term toMatch, List toMatchVariables, Term possibleMatch, Dictionary substitution); protected IdentifyCandidateMatchingTerm getMatchingSubstitution( Term toMatch, AtomicSentence expression) { IdentifyCandidateMatchingTerm icm = new IdentifyCandidateMatchingTerm( toMatch, expression, this); if (icm.isMatch()) { return icm; } // indicates no match return null; } protected class IdentifyCandidateMatchingTerm : FOLVisitor { private Term toMatch = null; private List toMatchVariables = null; private Term matchingTerm = null; private Dictionary substitution = null; private AbstractModulation abstractModulation; public IdentifyCandidateMatchingTerm(Term toMatch, AtomicSentence expression, AbstractModulation abstractModulation) { this.abstractModulation = abstractModulation; this.toMatch = toMatch; this.toMatchVariables = abstractModulation.variableCollector .collectAllVariables(toMatch); expression.accept(this, null); } public bool isMatch() { return null != matchingTerm; } public Term getMatchingTerm() { return matchingTerm; } public Dictionary getMatchingSubstitution() { return substitution; } // START-FOLVisitor public Object visitPredicate(Predicate p, Object arg) { foreach (Term t in p.getArgs()) { // Finish processing if have found a match if (null != matchingTerm) { break; } t.accept(this, null); } return p; } public Object visitTermEquality(TermEquality equality, Object arg) { foreach (Term t in equality.getArgs()) { // Finish processing if have found a match if (null != matchingTerm) { break; } t.accept(this, null); } return equality; } public Object visitVariable(Variable variable, Object arg) { if (null != (substitution = abstractModulation.unifier.unify(toMatch, variable))) { if (abstractModulation.isValidMatch(toMatch, toMatchVariables, variable, substitution)) { matchingTerm = variable; } } return variable; } public Object visitConstant(Constant constant, Object arg) { if (null != (substitution = abstractModulation.unifier.unify(toMatch, constant))) { if (abstractModulation.isValidMatch(toMatch, toMatchVariables, constant, substitution)) { matchingTerm = constant; } } return constant; } public Object visitFunction(Function function, Object arg) { if (null != (substitution = abstractModulation.unifier.unify(toMatch, function))) { if (abstractModulation.isValidMatch(toMatch, toMatchVariables, function, substitution)) { matchingTerm = function; } } if (null == matchingTerm) { // Try the Function's arguments foreach (Term t in function.getArgs()) { // Finish processing if have found a match if (null != matchingTerm) { break; } t.accept(this, null); } } return function; } public Object visitNotSentence(NotSentence sentence, Object arg) { throw new NotImplementedException( "visitNotSentence() should not be called."); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { throw new NotImplementedException( "visitConnectedSentence() should not be called."); } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { throw new NotImplementedException( "visitQuantifiedSentence() should not be called."); } // END-FOLVisitor } protected class ReplaceMatchingTerm : FOLVisitor { private Term toReplace = null; private Term replaceWith = null; private bool replaced = false; public ReplaceMatchingTerm() { } public AtomicSentence replace(AtomicSentence expression, Term toReplace, Term replaceWith) { this.toReplace = toReplace; this.replaceWith = replaceWith; return (AtomicSentence)expression.accept(this, null); } // // START-FOLVisitor public Object visitPredicate(Predicate p, Object arg) { List newTerms = new List(); foreach (Term t in p.getTerms()) { Term subsTerm = (Term)t.accept(this, arg); newTerms.Add(subsTerm); } return new Predicate(p.getPredicateName(), newTerms); } public Object visitTermEquality(TermEquality equality, Object arg) { Term newTerm1 = (Term)equality.getTerm1().accept(this, arg); Term newTerm2 = (Term)equality.getTerm2().accept(this, arg); return new TermEquality(newTerm1, newTerm2); } public Object visitVariable(Variable variable, Object arg) { if (!replaced) { if (toReplace.Equals(variable)) { replaced = true; return replaceWith; } } return variable; } public Object visitConstant(Constant constant, Object arg) { if (!replaced) { if (toReplace.Equals(constant)) { replaced = true; return replaceWith; } } return constant; } public Object visitFunction(Function function, Object arg) { if (!replaced) { if (toReplace.Equals(function)) { replaced = true; return replaceWith; } } List newTerms = new List(); foreach (Term t in function.getTerms()) { Term subsTerm = (Term)t.accept(this, arg); newTerms.Add(subsTerm); } return new Function(function.getFunctionName(), newTerms); } public Object visitNotSentence(NotSentence sentence, Object arg) { throw new NotImplementedException( "visitNotSentence() should not be called."); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { throw new NotImplementedException( "visitConnectedSentence() should not be called."); } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { throw new NotImplementedException( "visitQuantifiedSentence() should not be called."); } // END-FOLVisitor } } } ================================================ FILE: aima-csharp/logic/fol/inference/Demodulation.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using aima.core.logic.fol.inference.proof; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 354.
*
* Demodulation: For any terms x, y, and z, where z appears somewhere in literal * mi and where UNIFY(x,z) = θ:
* *
     *                 x=y,    m1 OR ... OR mn[z]
     *     ------------------------------------------------------------
     *     SUB(SUBST(θ,x), SUBST(θ,y), m1 OR ... OR mn)
     * 
* * where SUBST is the usual substitution of a binding list, and SUB(x,y,m) means * to replace x with y everywhere that x occurs within m.
*
* Some additional restrictions/clarifications highlighted in:
* Demodulation Restrictions
* 1. Unit Equations Only.
* 2. Variables substituted in Equation Only.
* * @author Ciaran O'Reilly * */ public class Demodulation : AbstractModulation { public Demodulation() { } public Clause apply(TermEquality assertion, Clause clExpression) { Clause altClExpression = null; foreach (Literal l1 in clExpression.getLiterals()) { AtomicSentence altExpression = apply(assertion, l1 .getAtomicSentence()); if (null != altExpression) { // I have an alternative, create a new clause // with the alternative and return List newLits = new List(); foreach (Literal l2 in clExpression.getLiterals()) { if (l1.Equals(l2)) { newLits.Add(l1.newInstance(altExpression)); } else { newLits.Add(l2); } } // Only apply demodulation at most once on // each call. altClExpression = new Clause(newLits); altClExpression.setProofStep(new ProofStepClauseDemodulation( altClExpression, clExpression, assertion)); if (clExpression.isImmutable()) { altClExpression.setImmutable(); } if (!clExpression.isStandardizedApartCheckRequired()) { altClExpression.setStandardizedApartCheckNotRequired(); } break; } } return altClExpression; } public AtomicSentence apply(TermEquality assertion, AtomicSentence expression) { AtomicSentence altExpression = null; IdentifyCandidateMatchingTerm icm = getMatchingSubstitution(assertion .getTerm1(), expression); if (null != icm) { Term replaceWith = substVisitor.subst( icm.getMatchingSubstitution(), assertion.getTerm2()); // Want to ignore reflexivity axiom situation, i.e. x = x if (!icm.getMatchingTerm().Equals(replaceWith)) { ReplaceMatchingTerm rmt = new ReplaceMatchingTerm(); // Only apply demodulation at most once on each call. altExpression = rmt.replace(expression, icm.getMatchingTerm(), replaceWith); } } return altExpression; } // PROTECTED METHODS public override bool isValidMatch(Term toMatch, List toMatchVariables, Term possibleMatch, Dictionary substitution) { // Demodulation only allows substitution in the equation only, // if the substitution contains variables not in the toMatch // side of the equation (i.e. left hand side), then // it is not a legal demodulation match. // Note: see: // http://logic.stanford.edu/classes/cs157/2008/lectures/lecture15.pdf // slide 23 for an example. bool contained = !substitution.Keys.Except(toMatchVariables).Any(); if (contained) { return true; } return false; } } } ================================================ FILE: aima-csharp/logic/fol/inference/FOLBCAsk.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using aima.core.logic.fol.inference.proof; using aima.core.logic.fol.kb; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference { /** * Artificial Intelligence A Modern Approach (2nd Edition): Figure 9.6, page * 288.
*
* *
     * function FOL-BC-ASK(KB, goals, theta) returns a set of substitutions
     *   input: KB, a knowledge base
     *          goals, a list of conjuncts forming a query (theta already applied)
     *          theta, the current substitution, initially the empty substitution {}
     *   local variables: answers, a set of substitutions, initially empty
     *   
     *   if goals is empty then return {theta}
     *   qDelta <- SUBST(theta, FIRST(goals))
     *   for each sentence r in KB where STANDARDIZE-APART(r) = (p1 ˆ ... ˆ pn => q)
     *          and thetaDelta <- UNIFY(q, qDelta) succeeds
     *       new_goals <- [p1,...,pn|REST(goals)]
     *       answers <- FOL-BC-ASK(KB, new_goals, COMPOSE(thetaDelta, theta)) U answers
     *   return answers
     * 
* * Figure 9.6 A simple backward-chaining algorithm. * * @author Ciaran O'Reilly * @author Mike Stampone */ public class FOLBCAsk : InferenceProcedure { public FOLBCAsk() { } // START-InferenceProcedure /** * Returns a set of substitutions * * @param KB * a knowledge base * @param query * goals, a list of conjuncts forming a query * * @return a set of substitutions */ public InferenceResult ask(FOLKnowledgeBase KB, Sentence query) { // Assertions on the type queries this Inference procedure // supports if (!(query is AtomicSentence)) { throw new ArgumentException( "Only Atomic Queries are supported."); } List goals = new List(); goals.Add(new Literal((AtomicSentence)query)); BCAskAnswerHandler ansHandler = new BCAskAnswerHandler(); List> allProofSteps = folbcask(KB, ansHandler, goals, new Dictionary()); ansHandler.setAllProofSteps(allProofSteps); return ansHandler; } // END-InferenceProcedure // PRIVATE METHODS /** * * function FOL-BC-ASK(KB, goals, theta) returns a set of substitutions * input: KB, a knowledge base * goals, a list of conjuncts forming a query (theta already applied) * theta, the current substitution, initially the empty substitution {} * */ private List> folbcask(FOLKnowledgeBase KB, BCAskAnswerHandler ansHandler, List goals, Dictionary theta) { List> thisLevelProofSteps = new List>(); // local variables: answers, a set of substitutions, initially empty // if goals is empty then return {theta} if (goals.Count == 0) { thisLevelProofSteps.Add(new List()); return thisLevelProofSteps; } // qDelta <- SUBST(theta, FIRST(goals)) Literal qDelta = KB.subst(theta, goals[0]); // for each sentence r in KB where // STANDARDIZE-APART(r) = (p1 ^ ... ^ pn => q) foreach (Clause r in KB.getAllDefiniteClauses()) { Clause r2 = KB.standardizeApart(r); // and thetaDelta <- UNIFY(q, qDelta) succeeds Dictionary thetaDelta = KB.unify(r2.getPositiveLiterals() [0].getAtomicSentence(), qDelta.getAtomicSentence()); if (null != thetaDelta) { // new_goals <- [p1,...,pn|REST(goals)] List newGoals = new List(r2 .getNegativeLiterals()); newGoals.AddRange(goals.Skip(1)); // answers <- FOL-BC-ASK(KB, new_goals, COMPOSE(thetaDelta, // theta)) U answers Dictionary composed = compose(KB, thetaDelta, theta); List> lowerLevelProofSteps = folbcask( KB, ansHandler, newGoals, composed); ansHandler.addProofStep(lowerLevelProofSteps, r2, qDelta, composed); thisLevelProofSteps.AddRange(lowerLevelProofSteps); } } // return answers return thisLevelProofSteps; } // Artificial Intelligence A Modern Approach (2nd Edition): page 288. // COMPOSE(delta, tau) is the substitution whose effect is identical to // the effect of applying each substitution in turn. That is, // SUBST(COMPOSE(theta1, theta2), p) = SUBST(theta2, SUBST(theta1, p)) private Dictionary compose(FOLKnowledgeBase KB, Dictionary theta1, Dictionary theta2) { Dictionary composed = new Dictionary(); // So that it behaves like: // SUBST(theta2, SUBST(theta1, p)) // There are two steps involved here. // See: http://logic.stanford.edu/classes/cs157/2008/notes/chap09.pdf // for a detailed discussion: // 1. Apply theta2 to the range of theta1. foreach (Variable v in theta1.Keys) { composed.Add(v, KB.subst(theta2, theta1[v])); } // 2. Adjoin to delta all pairs from tau with different // domain variables. foreach (Variable v in theta2.Keys) { if (!theta1.ContainsKey(v)) { composed.Add(v, theta2[v]); } } return cascadeSubstitutions(KB, composed); } // See: // http://logic.stanford.edu/classes/cs157/2008/miscellaneous/faq.html#jump165 // for need for this. private Dictionary cascadeSubstitutions(FOLKnowledgeBase KB, Dictionary theta) { foreach (Variable v in theta.Keys) { Term t = theta[v]; theta.Add(v, KB.subst(theta, t)); } return theta; } class BCAskAnswerHandler : InferenceResult { private List proofs = new List(); public BCAskAnswerHandler() { } // START-InferenceResult public bool isPossiblyFalse() { return proofs.Count == 0; } public bool isTrue() { return proofs.Count > 0; } public bool isUnknownDueToTimeout() { return false; } public bool isPartialResultDueToTimeout() { return false; } public List getProofs() { return proofs; } // END-InferenceResult public void setAllProofSteps(List> allProofSteps) { foreach (List steps in allProofSteps) { ProofStepBwChGoal lastStep = steps[steps.Count - 1]; Dictionary theta = lastStep.getBindings(); proofs.Add(new ProofFinal(lastStep, theta)); } } public void addProofStep( List> currentLevelProofSteps, Clause toProve, Literal currentGoal, Dictionary bindings) { if (currentLevelProofSteps.Count > 0) { ProofStepBwChGoal predecessor = new ProofStepBwChGoal(toProve, currentGoal, bindings); foreach (List steps in currentLevelProofSteps) { if (steps.Count > 0) { steps[0].setPredecessor(predecessor); } steps.Insert(0, predecessor); } } } } } } ================================================ FILE: aima-csharp/logic/fol/inference/FOLFCAsk.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.inference.proof; using aima.core.logic.fol.kb; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 9.3, page 332. * *
     * function FOL-FC-ASK(KB, alpha) returns a substitution or false
     *   inputs: KB, the knowledge base, a set of first order definite clauses
     *           alpha, the query, an atomic sentence
     *   local variables: new, the new sentences inferred on each iteration
     *   
     *   repeat until new is empty
     *      new <- {}
     *      for each rule in KB do
     *          (p1 ^ ... ^ pn => q) <- STANDARDIZE-VARAIBLES(rule)
     *          for each theta such that SUBST(theta, p1 ^ ... ^ pn) = SUBST(theta, p'1 ^ ... ^ p'n)
     *                         for some p'1,...,p'n in KB
     *              q' <- SUBST(theta, q)
     *              if q' does not unify with some sentence already in KB or new then
     *                   add q' to new
     *                   theta <- UNIFY(q', alpha)
     *                   if theta is not fail then return theta
     *      add new to KB
     *   return false
     * 
* * Figure 9.3 A conceptually straightforward, but very inefficient forward-chaining algo- * rithm. On each iteration, it adds to KB all the atomic sentences that can be inferred in one * step from the implication sentences and the atomic sentences already in KB. The function * STANDARDIZE-VARIABLES replaces all variables in its arguments with new ones that have * not been used before. */ /** * @author Ciaran O'Reilly * */ public class FOLFCAsk : InferenceProcedure { public FOLFCAsk() { } // START-InferenceProcedure /** * * function FOL-FC-ASK(KB, alpha) returns a substitution or false * inputs: KB, the knowledge base, a set of first order definite clauses * alpha, the query, an atomic sentence * */ public InferenceResult ask(FOLKnowledgeBase KB, Sentence query) { // Assertions on the type of queries this Inference procedure // supports if (!(query is AtomicSentence)) { throw new ArgumentException( "Only Atomic Queries are supported."); } FCAskAnswerHandler ansHandler = new FCAskAnswerHandler(); Literal alpha = new Literal((AtomicSentence)query); // local variables: new, the new sentences inferred on each iteration List newSentences = new List(); // Ensure query is not already a know fact before // attempting forward chaining. List> answers = KB.fetch(alpha); if (answers.Count > 0) { ansHandler.addProofStep(new ProofStepFoChAlreadyAFact(alpha)); ansHandler.setAnswers(answers); return ansHandler; } // repeat until new is empty do { // new <- {} newSentences.Clear(); // for each rule in KB do // (p1 ^ ... ^ pn => q) <-STANDARDIZE-VARIABLES(rule) foreach (Clause impl in KB.getAllDefiniteClauseImplications()) { Clause impl2 = KB.standardizeApart(impl); // for each theta such that SUBST(theta, p1 ^ ... ^ pn) = // SUBST(theta, p'1 ^ ... ^ p'n) // --- for some p'1,...,p'n in KB foreach (Dictionary theta in KB.fetch(invert(new List(impl2 .getNegativeLiterals())))) { // q' <- SUBST(theta, q) Literal qPrime = KB.subst(theta, impl.getPositiveLiterals() [0]); // if q' does not unify with some sentence already in KB or // new then do if (!KB.isRenaming(qPrime) && !KB.isRenaming(qPrime, newSentences)) { // add q' to new newSentences.Add(qPrime); ansHandler.addProofStep(impl, qPrime, theta); // theta <- UNIFY(q', alpha) Dictionary theta2 = KB.unify(qPrime.getAtomicSentence(), alpha .getAtomicSentence()); // if theta is not fail then return theta if (null != theta2) { foreach (Literal l in newSentences) { Sentence s = null; if (l.isPositiveLiteral()) { s = l.getAtomicSentence(); } else { s = new NotSentence(l.getAtomicSentence()); } KB.tell(s); } ansHandler.setAnswers(KB.fetch(alpha)); return ansHandler; } } } } // add new to KB foreach (Literal l in newSentences) { Sentence s = null; if (l.isPositiveLiteral()) { s = l.getAtomicSentence(); } else { s = new NotSentence(l.getAtomicSentence()); } KB.tell(s); } } while (newSentences.Count > 0); // return false return ansHandler; } // END-InferenceProcedure // PRIVATE METHODS private List invert(List lits) { List invLits = new List(); foreach (Literal l in lits) { invLits.Add(new Literal(l.getAtomicSentence(), (l .isPositiveLiteral() ? true : false))); } return invLits; } class FCAskAnswerHandler : InferenceResult { private ProofStep stepFinal = null; private List proofs = new List(); public FCAskAnswerHandler() { } // START-InferenceResult public bool isPossiblyFalse() { return proofs.Count == 0; } public bool isTrue() { return proofs.Count > 0; } public bool isUnknownDueToTimeout() { return false; } public bool isPartialResultDueToTimeout() { return false; } public List getProofs() { return proofs; } // END-InferenceResult public void addProofStep(Clause implication, Literal fact, Dictionary bindings) { stepFinal = new ProofStepFoChAssertFact(implication, fact, bindings, stepFinal); } public void addProofStep(ProofStep step) { stepFinal = step; } public void setAnswers(List> answers) { foreach (Dictionary ans in answers) { proofs.Add(new ProofFinal(stepFinal, ans)); } } } } } ================================================ FILE: aima-csharp/logic/fol/inference/FOLModelElimination.cs ================================================ using System; using System.Collections.Generic; using System.Text; using aima.core.logic.fol.kb; using aima.core.logic.fol.parsing; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; using aima.core.logic.fol.inference.proof; using aima.core.logic.fol.inference.trace; namespace aima.core.logic.fol.inference { /** * Based on lecture notes from:
* * http://logic.stanford.edu/classes/cs157/2008/lectures/lecture13.pdf * * @author Ciaran O'Reilly * */ public class FOLModelElimination : InferenceProcedure { // Ten seconds is default maximum query time permitted private long maxQueryTime = 10 * 1000; private FOLModelEliminationTracer tracer = null; private Unifier unifier = new Unifier(); private SubstVisitor substVisitor = new SubstVisitor(); public FOLModelElimination() { } public FOLModelElimination(long maxQueryTime) { setMaxQueryTime(maxQueryTime); } public FOLModelElimination(FOLModelEliminationTracer tracer) { this.tracer = tracer; } public FOLModelElimination(FOLModelEliminationTracer tracer, long maxQueryTime) { this.tracer = tracer; setMaxQueryTime(maxQueryTime); } public long getMaxQueryTime() { return maxQueryTime; } public void setMaxQueryTime(long maxQueryTime) { this.maxQueryTime = maxQueryTime; } // START-InferenceProcedure public InferenceResult ask(FOLKnowledgeBase kb, Sentence aQuery) { // Get the background knowledge - are assuming this is satisfiable // as using Set of Support strategy. List bgClauses = new List(kb.getAllClauses()); List removeList = SubsumptionElimination.findSubsumedClauses(bgClauses); foreach (Clause c in removeList) { bgClauses.Remove(c); } List background = createChainsFromClauses(bgClauses); // Collect the information necessary for constructing // an answer (supports use of answer literals). AnswerHandler ansHandler = new AnswerHandler(kb, aQuery, maxQueryTime, this); IndexedFarParents ifps = new IndexedFarParents(ansHandler .getSetOfSupport(), background); // Iterative deepening to be used for (int maxDepth = 1; maxDepth < int.MaxValue; maxDepth++) { // Track the depth actually reached ansHandler.resetMaxDepthReached(); if (null != tracer) { tracer.reset(); } foreach (Chain nearParent in ansHandler.getSetOfSupport()) { recursiveDLS(maxDepth, 0, nearParent, ifps, ansHandler); if (ansHandler.isComplete()) { return ansHandler; } } // This means the search tree // has bottomed out (i.e. finite). // Return what I know based on exploring everything. if (ansHandler.getMaxDepthReached() < maxDepth) { return ansHandler; } } return ansHandler; } // END-InferenceProcedure // PRIVATE METHODS public List createChainsFromClauses(List clauses) { List chains = new List(); foreach (Clause c in clauses) { Chain chn = new Chain(c.getLiterals()); chn.setProofStep(new ProofStepChainFromClause(chn, c)); chains.Add(chn); chains.AddRange(chn.getContrapositives()); } return chains; } // Recursive Depth Limited Search private void recursiveDLS(int maxDepth, int currentDepth, Chain nearParent, IndexedFarParents indexedFarParents, AnswerHandler ansHandler) { // Keep track of the maximum depth reached. ansHandler.updateMaxDepthReached(currentDepth); if (currentDepth == maxDepth) { return; } int noCandidateFarParents = indexedFarParents .getNumberCandidateFarParents(nearParent); if (null != tracer) { tracer.increment(currentDepth, noCandidateFarParents); } indexedFarParents.standardizeApart(nearParent); for (int farParentIdx = 0; farParentIdx < noCandidateFarParents; farParentIdx++) { // If have a complete answer, don't keep // checking candidate far parents if (ansHandler.isComplete()) { break; } // Reduction Chain nextNearParent = indexedFarParents.attemptReduction( nearParent, farParentIdx); if (null == nextNearParent) { // Unable to remove the head via reduction continue; } // Handle Canceling and Dropping bool cancelled = false; bool dropped = false; do { cancelled = false; Chain nextParent = null; while (nextNearParent != (nextParent = tryCancellation(nextNearParent))) { nextNearParent = nextParent; cancelled = true; } dropped = false; while (nextNearParent != (nextParent = tryDropping(nextNearParent))) { nextNearParent = nextParent; dropped = true; } } while (dropped || cancelled); // Check if have answer before // going to the next level if (!ansHandler.isAnswer(nextNearParent)) { // Keep track of the current # of // far parents that are possible for the next near parent. int noNextFarParents = indexedFarParents .getNumberFarParents(nextNearParent); // Add to indexed far parents nextNearParent = indexedFarParents.addToIndex(nextNearParent); // Check the next level recursiveDLS(maxDepth, currentDepth + 1, nextNearParent, indexedFarParents, ansHandler); // Reset the number of far parents possible // when recursing back up. indexedFarParents.resetNumberFarParentsTo(nextNearParent, noNextFarParents); } } } // Returns c if no cancellation occurred private Chain tryCancellation(Chain c) { Literal head = c.getHead(); if (null != head && !(head is ReducedLiteral)) { foreach (Literal l in c.getTail()) { if (l is ReducedLiteral) { // if they can be resolved if (head.isNegativeLiteral() != l.isNegativeLiteral()) { Dictionary subst = unifier.unify(head .getAtomicSentence(), l.getAtomicSentence()); if (null != subst) { // I have a cancellation // Need to apply subst to all of the // literals in the cancellation List cancLits = new List(); foreach (Literal lfc in c.getTail()) { AtomicSentence a = (AtomicSentence)substVisitor .subst(subst, lfc.getAtomicSentence()); cancLits.Add(lfc.newInstance(a)); } Chain cancellation = new Chain(cancLits); cancellation .setProofStep(new ProofStepChainCancellation( cancellation, c, subst)); return cancellation; } } } } } return c; } // Returns c if no dropping occurred private Chain tryDropping(Chain c) { Literal head = c.getHead(); if (null != head && (head is ReducedLiteral)) { Chain dropped = new Chain(c.getTail()); dropped.setProofStep(new ProofStepChainDropped(dropped, c)); return dropped; } return c; } class AnswerHandler : InferenceResult { private Chain answerChain = new Chain(); private List answerLiteralVariables; private List sos = null; private bool complete = false; private long finishTime = 0L; private int maxDepthReached = 0; private List proofs = new List(); private bool timedOut = false; public AnswerHandler(FOLKnowledgeBase kb, Sentence aQuery, long maxQueryTime, FOLModelElimination folModelElimination) { finishTime = System.DateTime.UtcNow.Ticks + maxQueryTime; Sentence refutationQuery = new NotSentence(aQuery); // Want to use an answer literal to pull // query variables where necessary Literal answerLiteral = kb.createAnswerLiteral(refutationQuery); answerLiteralVariables = kb.collectAllVariables(answerLiteral .getAtomicSentence()); // Create the Set of Support based on the Query. if (answerLiteralVariables.Count > 0) { Sentence refutationQueryWithAnswer = new ConnectedSentence( Connectors.OR, refutationQuery, (Sentence)answerLiteral .getAtomicSentence().copy()); sos = folModelElimination.createChainsFromClauses(kb .convertToClauses(refutationQueryWithAnswer)); answerChain.addLiteral(answerLiteral); } else { sos = folModelElimination.createChainsFromClauses(kb .convertToClauses(refutationQuery)); } foreach (Chain s in sos) { s.setProofStep(new ProofStepGoal(s)); } } // START-InferenceResult public bool isPossiblyFalse() { return !timedOut && proofs.Count == 0; } public bool isTrue() { return proofs.Count > 0; } public bool isUnknownDueToTimeout() { return timedOut && proofs.Count == 0; } public bool isPartialResultDueToTimeout() { return timedOut && proofs.Count > 0; } public List getProofs() { return proofs; } // END-InferenceResult public List getSetOfSupport() { return sos; } public bool isComplete() { return complete; } public void resetMaxDepthReached() { maxDepthReached = 0; } public int getMaxDepthReached() { return maxDepthReached; } public void updateMaxDepthReached(int depth) { if (depth > maxDepthReached) { maxDepthReached = depth; } } public bool isAnswer(Chain nearParent) { bool isAns = false; if (answerChain.isEmpty()) { if (nearParent.isEmpty()) { proofs.Add(new ProofFinal(nearParent.getProofStep(), new Dictionary())); complete = true; isAns = true; } } else { if (nearParent.isEmpty()) { // This should not happen // as added an answer literal to sos, which // implies the database (i.e. premises) are // unsatisfiable to begin with. throw new ApplicationException( "Generated an empty chain while looking for an answer, implies original KB is unsatisfiable"); } if (1 == nearParent.getNumberLiterals() && nearParent.getHead().getAtomicSentence() .getSymbolicName().Equals( answerChain.getHead() .getAtomicSentence() .getSymbolicName())) { Dictionary answerBindings = new Dictionary(); List answerTerms = nearParent.getHead() .getAtomicSentence().getArgs(); int idx = 0; foreach (Variable v in answerLiteralVariables) { answerBindings.Add(v, (Term)answerTerms[idx]); idx++; } bool addNewAnswer = true; foreach (Proof p in proofs) { if (p.getAnswerBindings().Equals(answerBindings)) { addNewAnswer = false; break; } } if (addNewAnswer) { proofs.Add(new ProofFinal(nearParent.getProofStep(), answerBindings)); } isAns = true; } } if (DateTime.Now.Ticks > finishTime) { complete = true; // Indicate that I have run out of query time timedOut = true; } return isAns; } public override String ToString() { StringBuilder sb = new StringBuilder(); sb.Append("isComplete=" + complete); sb.Append("\n"); sb.Append("result=" + proofs); return sb.ToString(); } } } class IndexedFarParents { private int saIdx = 0; private Unifier unifier = new Unifier(); private SubstVisitor substVisitor = new SubstVisitor(); private Dictionary> posHeads = new Dictionary>(); private Dictionary> negHeads = new Dictionary>(); public IndexedFarParents(List sos, List background) { constructInternalDataStructures(sos, background); } public int getNumberFarParents(Chain farParent) { Literal head = farParent.getHead(); Dictionary> heads = null; if (head.isPositiveLiteral()) { heads = posHeads; } else { heads = negHeads; } String headKey = head.getAtomicSentence().getSymbolicName(); List farParents = heads[headKey]; if (null != farParents) { return farParents.Count; } return 0; } public void resetNumberFarParentsTo(Chain farParent, int toSize) { Literal head = farParent.getHead(); Dictionary> heads = null; if (head.isPositiveLiteral()) { heads = posHeads; } else { heads = negHeads; } String key = head.getAtomicSentence().getSymbolicName(); List farParents = heads[key]; while (farParents.Count > toSize) { farParents.RemoveAt(farParents.Count - 1); } } public int getNumberCandidateFarParents(Chain nearParent) { Literal nearestHead = nearParent.getHead(); Dictionary> candidateHeads = null; if (nearestHead.isPositiveLiteral()) { candidateHeads = negHeads; } else { candidateHeads = posHeads; } String nearestKey = nearestHead.getAtomicSentence().getSymbolicName(); List farParents = candidateHeads[nearestKey]; if (null != farParents) { return farParents.Count; } return 0; } public Chain attemptReduction(Chain nearParent, int farParentIndex) { Chain nnpc = null; Literal nearLiteral = nearParent.getHead(); Dictionary> candidateHeads = null; if (nearLiteral.isPositiveLiteral()) { candidateHeads = negHeads; } else { candidateHeads = posHeads; } AtomicSentence nearAtom = nearLiteral.getAtomicSentence(); String nearestKey = nearAtom.getSymbolicName(); List farParents = candidateHeads[nearestKey]; if (null != farParents) { Chain farParent = farParents[farParentIndex]; standardizeApart(farParent); Literal farLiteral = farParent.getHead(); AtomicSentence farAtom = farLiteral.getAtomicSentence(); Dictionary subst = unifier.unify(nearAtom, farAtom); // If I was able to unify with one // of the far heads if (null != subst) { // Want to always apply reduction uniformly Chain topChain = farParent; Literal botLit = nearLiteral; Chain botChain = nearParent; // Need to apply subst to all of the // literals in the reduction List reduction = new List(); foreach (Literal l in topChain.getTail()) { AtomicSentence atom = (AtomicSentence)substVisitor.subst( subst, l.getAtomicSentence()); reduction.Add(l.newInstance(atom)); } reduction.Add(new ReducedLiteral((AtomicSentence)substVisitor .subst(subst, botLit.getAtomicSentence()), botLit .isNegativeLiteral())); foreach (Literal l in botChain.getTail()) { AtomicSentence atom = (AtomicSentence)substVisitor.subst( subst, l.getAtomicSentence()); reduction.Add(l.newInstance(atom)); } nnpc = new Chain(reduction); nnpc.setProofStep(new ProofStepChainReduction(nnpc, nearParent, farParent, subst)); } } return nnpc; } public Chain addToIndex(Chain c) { Chain added = null; Literal head = c.getHead(); if (null != head) { Dictionary> toAddTo = null; if (head.isPositiveLiteral()) { toAddTo = posHeads; } else { toAddTo = negHeads; } String key = head.getAtomicSentence().getSymbolicName(); List farParents = toAddTo[key]; if (null == farParents) { farParents = new List(); toAddTo.Add(key, farParents); } added = c; farParents.Add(added); } return added; } public void standardizeApart(Chain c) { saIdx = StandardizeApartInPlace.standardizeApart(c, saIdx); } public override String ToString() { StringBuilder sb = new StringBuilder(); sb.Append("#"); sb.Append(posHeads.Count); foreach (String key in posHeads.Keys) { sb.Append(","); sb.Append(posHeads[key].Count); } sb.Append(" posHeads="); sb.Append(posHeads.ToString()); sb.Append("\n"); sb.Append("#"); sb.Append(negHeads.Count); foreach (String key in negHeads.Keys) { sb.Append(","); sb.Append(negHeads[key].Count); } sb.Append(" negHeads="); sb.Append(negHeads.ToString()); return sb.ToString(); } // PRIVATE METHODS private void constructInternalDataStructures(List sos, List background) { List toIndex = new List(); toIndex.AddRange(sos); toIndex.AddRange(background); foreach (Chain c in toIndex) { addToIndex(c); } } } } ================================================ FILE: aima-csharp/logic/fol/inference/FOLOTTERLikeTheoremProver.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using aima.core.logic.fol.inference.otter; using aima.core.logic.fol.inference.otter.defaultimpl; using aima.core.logic.fol.inference.proof; using aima.core.logic.fol.kb; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; using aima.core.logic.fol; namespace aima.core.logic.fol.inference { /** * Artificial Intelligence A Modern Approach (2nd Edition): Figure 9.14, page * 307.
*
* *
 * procedure OTTER(sos, usable)
 *   inputs: sos, a set of support-clauses defining the problem (a global variable)
 *   usable, background knowledge potentially relevant to the problem
 *   
 *   repeat
 *      clause <- the lightest member of sos
 *      move clause from sos to usable
 *      PROCESS(INFER(clause, usable), sos)
 *   until sos = [] or a refutation has been found
 * 
 * --------------------------------------------------------------------------------
 * 
 * function INFER(clause, usable) returns clauses
 *   
 *   resolve clause with each member of usable
 *   return the resulting clauses after applying filter
 *   
 * --------------------------------------------------------------------------------
 * 
 * procedure PROCESS(clauses, sos)
 * 
 *   for each clause in clauses do
 *       clause <- SIMPLIFY(clause)
 *       merge identical literals
 *       discard clause if it is a tautology
 *       sos <- [clause | sos]
 *       if clause has no literals then a refutation has been found
 *       if clause has one literal then look for unit refutation
 * 
* * Figure 9.14 Sketch of the OTTER theorem prover. Heuristic control is applied * in the selection of the "lightest" clause and in the FILTER function that * eliminates uninteresting clauses from consideration.
*
* Note: The original implementation of OTTER has been retired but its * successor, Prover9, can be found at:
* http://www.prover9.org/
* or
* http://www.cs.unm.edu/~mccune/ * mace4/
* Should you wish to play with a mature implementation of a theorem prover :-)
*
* For lots of interesting problems to play with, see The TPTP Problem * Library for Automated Theorem Proving:
* http://www.cs.miami.edu/~tptp/
* * @author Ciaran O'Reilly * */ public class FOLOTTERLikeTheoremProver : InferenceProcedure { // Ten seconds is default maximum query time permitted private long maxQueryTime = 10 * 1000; private bool useParamodulation = true; private LightestClauseHeuristic lightestClauseHeuristic = new DefaultLightestClauseHeuristic(); private ClauseFilter clauseFilter = new DefaultClauseFilter(); private ClauseSimplifier clauseSimplifier = new DefaultClauseSimplifier(); private Paramodulation paramodulation = new Paramodulation(); public FOLOTTERLikeTheoremProver() { } public FOLOTTERLikeTheoremProver(long maxQueryTime) { setMaxQueryTime(maxQueryTime); } public FOLOTTERLikeTheoremProver(bool useParamodulation) { setUseParamodulation(useParamodulation); } public FOLOTTERLikeTheoremProver(long maxQueryTime, bool useParamodulation) { setMaxQueryTime(maxQueryTime); setUseParamodulation(useParamodulation); } public long getMaxQueryTime() { return maxQueryTime; } public void setMaxQueryTime(long maxQueryTime) { this.maxQueryTime = maxQueryTime; } public bool isUseParamodulation() { return useParamodulation; } public void setUseParamodulation(bool useParamodulation) { this.useParamodulation = useParamodulation; } public LightestClauseHeuristic getLightestClauseHeuristic() { return lightestClauseHeuristic; } public void setLightestClauseHeuristic( LightestClauseHeuristic lightestClauseHeuristic) { this.lightestClauseHeuristic = lightestClauseHeuristic; } public ClauseFilter getClauseFilter() { return clauseFilter; } public void setClauseFilter(ClauseFilter clauseFilter) { this.clauseFilter = clauseFilter; } public ClauseSimplifier getClauseSimplifier() { return clauseSimplifier; } public void setClauseSimplifier(ClauseSimplifier clauseSimplifier) { this.clauseSimplifier = clauseSimplifier; } // START-InferenceProcedure public InferenceResult ask(FOLKnowledgeBase KB, Sentence alpha) { List sos = new List(); List usable = new List(); // Usable set will be the set of clauses in the KB, // are assuming this is satisfiable as using the // Set of Support strategy. foreach (Clause c in KB.getAllClauses()) { Clause c2 = KB.standardizeApart(c); c2.setStandardizedApartCheckNotRequired(); usable.AddRange(c2.getFactors()); } // Ensure reflexivity axiom is added to usable if using paramodulation. if (isUseParamodulation()) { // Reflexivity Axiom: x = x TermEquality reflexivityAxiom = new TermEquality(new Variable("x"), new Variable("x")); Clause reflexivityClause = new Clause(); reflexivityClause.addLiteral(new Literal(reflexivityAxiom)); reflexivityClause = KB.standardizeApart(reflexivityClause); reflexivityClause.setStandardizedApartCheckNotRequired(); usable.Add(reflexivityClause); } Sentence notAlpha = new NotSentence(alpha); // Want to use an answer literal to pull // query variables where necessary Literal answerLiteral = KB.createAnswerLiteral(notAlpha); List answerLiteralVariables = KB .collectAllVariables(answerLiteral.getAtomicSentence()); Clause answerClause = new Clause(); if (answerLiteralVariables.Count > 0) { Sentence notAlphaWithAnswer = new ConnectedSentence(Connectors.OR, notAlpha, answerLiteral.getAtomicSentence()); foreach (Clause c in KB.convertToClauses(notAlphaWithAnswer)) { Clause c2 = KB.standardizeApart(c); c2.setProofStep(new ProofStepGoal(c2)); c2.setStandardizedApartCheckNotRequired(); sos.AddRange(c2.getFactors()); } answerClause.addLiteral(answerLiteral); } else { foreach (Clause c in KB.convertToClauses(notAlpha)) { Clause c2 = KB.standardizeApart(c); c2.setProofStep(new ProofStepGoal(c2)); c2.setStandardizedApartCheckNotRequired(); sos.AddRange(c2.getFactors()); } } // Ensure all subsumed clauses are removed foreach (Clause c in SubsumptionElimination.findSubsumedClauses(usable)) { usable.Remove(c); } foreach (Clause c in SubsumptionElimination.findSubsumedClauses(sos)) { sos.Remove(c); } OTTERAnswerHandler ansHandler = new OTTERAnswerHandler(answerLiteral, answerLiteralVariables, answerClause, maxQueryTime); IndexedClauses idxdClauses = new IndexedClauses( getLightestClauseHeuristic(), sos, usable); return otter(ansHandler, idxdClauses, sos, usable); } // END-InferenceProcedure /** *
         * procedure OTTER(sos, usable) 
         *   inputs: sos, a set of support-clauses defining the problem (a global variable) 
         *   usable, background knowledge potentially relevant to the problem
         * 
*/ private InferenceResult otter(OTTERAnswerHandler ansHandler, IndexedClauses idxdClauses, List sos, List usable) { getLightestClauseHeuristic().initialSOS(sos); // * repeat do { // * clause <- the lightest member of sos Clause clause = getLightestClauseHeuristic().getLightestClause(); if (null != clause) { // * move clause from sos to usable sos.Remove(clause); getLightestClauseHeuristic().removedClauseFromSOS(clause); usable.Add(clause); // * PROCESS(INFER(clause, usable), sos) process(ansHandler, idxdClauses, infer(clause, usable), sos, usable); } // * until sos = [] or a refutation has been found } while (sos.Count != 0 && !ansHandler.isComplete()); return ansHandler; } /** *
         * function INFER(clause, usable) returns clauses
         */
	private List infer(Clause clause, List usable)
	{
	    List resultingClauses = new List();
	    // * resolve clause with each member of usable
	    foreach (Clause c in usable)
	    {
		List resolvents = clause.binaryResolvents(c);
		foreach (Clause rc in resolvents)
		{
		    resultingClauses.Add(rc);
		}

		// if using paramodulation to handle equality
		if (isUseParamodulation())
		{
		    List paras = paramodulation.apply(clause, c, true);
		    foreach (Clause p in paras)
		    {
			resultingClauses.Add(p);
		    }
		}
	    }

	    // * return the resulting clauses after applying filter
	    return getClauseFilter().filter(new HashSet(resultingClauses)).ToList();
	}

	// procedure PROCESS(clauses, sos)
	private void process(OTTERAnswerHandler ansHandler,
		IndexedClauses idxdClauses, List clauses, List sos,
		List usable)
	{

	    // * for each clause in clauses do
	    foreach (Clause clause in clauses)
	    {
		// * clause <- SIMPLIFY(clause)
		Clause clause2 = getClauseSimplifier().simplify(clause);

		// * merge identical literals
		// Note: Not required as handled by Clause Implementation
		// which keeps literals within a Set, so no duplicates
		// will exist.

		// * discard clause if it is a tautology
		if (clause2.isTautology())
		{
		    continue;
		}

		// * if clause has no literals then a refutation has been found
		// or if it just contains the answer literal.
		if (!ansHandler.isAnswer(clause2))
		{
		    // * sos <- [clause | sos]
		    // This check ensure duplicate clauses are not
		    // introduced which will cause the
		    // LightestClauseHeuristic to loop continuously
		    // on the same pair of objects.
		    if (!sos.Contains(clause2) && !usable.Contains(clause2))
		    {
			foreach (Clause ac in clause2.getFactors())
			{
			    if (!sos.Contains(ac) && !usable.Contains(ac))
			    {
				idxdClauses.addClause(ac, sos, usable);

				// * if clause has one literal then look for unit
				// refutation
				lookForUnitRefutation(ansHandler, idxdClauses, ac,
					sos, usable);
			    }
			}
		    }
		}

		if (ansHandler.isComplete())
		{
		    break;
		}
	    }
	}

	private void lookForUnitRefutation(OTTERAnswerHandler ansHandler,
		IndexedClauses idxdClauses, Clause clause, List sos,
		List usable)
	{

	    List toCheck = new List();

	    if (ansHandler.isCheckForUnitRefutation(clause))
	    {
		foreach (Clause s in sos)
		{
		    if (s.isUnitClause())
		    {
			toCheck.Add(s);
		    }
		}
		foreach (Clause u in usable)
		{
		    if (u.isUnitClause())
		    {
			toCheck.Add(u);
		    }
		}
	    }

	    if (toCheck.Count > 0)
	    {
		toCheck = infer(clause, toCheck);
		foreach (Clause t in toCheck)
		{
		    // * clause <- SIMPLIFY(clause)
		    Clause t2 = getClauseSimplifier().simplify(t);

		    // * discard clause if it is a tautology
		    if (t2.isTautology())
		    {
			continue;
		    }

		    // * if clause has no literals then a refutation has been found
		    // or if it just contains the answer literal.
		    if (!ansHandler.isAnswer(t2))
		    {
			// * sos <- [clause | sos]
			// This check ensure duplicate clauses are not
			// introduced which will cause the
			// LightestClauseHeuristic to loop continuously
			// on the same pair of objects.
			if (!sos.Contains(t2) && !usable.Contains(t2))
			{
			    idxdClauses.addClause(t2, sos, usable);
			}
		    }

		    if (ansHandler.isComplete())
		    {
			break;
		    }
		}
	    }
	}

	// This is a simple indexing on the clauses to support
	// more efficient forward and backward subsumption testing.
	class IndexedClauses
	{
	    private LightestClauseHeuristic lightestClauseHeuristic = null;
	    // Group the clauses by their # of literals.
	    private Dictionary> clausesGroupedBySize = new Dictionary>();
	    // Keep track of the min and max # of literals.
	    private int minNoLiterals = int.MaxValue;
	    private int maxNoLiterals = 0;

	    public IndexedClauses(LightestClauseHeuristic lightestClauseHeuristic,
		    List sos, List usable)
	    {
		this.lightestClauseHeuristic = lightestClauseHeuristic;
		foreach (Clause c in sos)
		{
		    indexClause(c);
		}
		foreach (Clause c in usable)
		{
		    indexClause(c);
		}
	    }

	    public void addClause(Clause c, List sos, List usable)
	    {
		// Perform forward subsumption elimination
		bool addToSOS = true;
		for (int i = minNoLiterals; i < c.getNumberLiterals(); i++)
		{
		    List fs = clausesGroupedBySize[i];
		    if (null != fs)
		    {
			foreach (Clause s in fs)
			{
			    if (s.subsumes(c))
			    {
				addToSOS = false;
				break;
			    }
			}
		    }
		    if (!addToSOS)
		    {
			break;
		    }
		}

		if (addToSOS)
		{
		    sos.Add(c);
		    lightestClauseHeuristic.addedClauseToSOS(c);
		    indexClause(c);
		    // Have added clause, therefore
		    // perform backward subsumption elimination
		    List subsumed = new List();
		    for (int i = c.getNumberLiterals() + 1; i <= maxNoLiterals; i++)
		    {
			subsumed.Clear();
			List bs = clausesGroupedBySize[i];
			if (null != bs)
			{
			    foreach (Clause s in bs)
			    {
				if (c.subsumes(s))
				{
				    subsumed.Add(s);
				    if (sos.Contains(s))
				    {
					sos.Remove(s);
					lightestClauseHeuristic
						.removedClauseFromSOS(s);
				    }
				    usable.Remove(s);
				}
			    }
			    foreach (Clause c2 in subsumed)
			    {
				bs.Remove(c2);
			    }
			}
		    }
		}
	    }

	    // PRIVATE METHODS

	    private void indexClause(Clause c)
	    {
		int size = c.getNumberLiterals();
		if (size < minNoLiterals)
		{
		    minNoLiterals = size;
		}
		if (size > maxNoLiterals)
		{
		    maxNoLiterals = size;
		}
		List cforsize = null;
		if (!clausesGroupedBySize.ContainsKey(size))
		{
		    cforsize = new List();
		    clausesGroupedBySize.Add(size, cforsize);
		}
		else
		{
		    cforsize = clausesGroupedBySize[size];
		}
		cforsize.Add(c);
	    }
	}

	class OTTERAnswerHandler : InferenceResult
	{
	    private Literal answerLiteral = null;
	    private List answerLiteralVariables = null;
	    private Clause answerClause = null;
	    private long finishTime = 0L;
	    private bool complete = false;
	    private List proofs = new List();
	    private bool timedOut = false;

	    public OTTERAnswerHandler(Literal answerLiteral,
		    List answerLiteralVariables, Clause answerClause,
		    long maxQueryTime)
	    {
		this.answerLiteral = answerLiteral;
		this.answerLiteralVariables = answerLiteralVariables;
		this.answerClause = answerClause;
		//
		this.finishTime = DateTime.UtcNow.Ticks + maxQueryTime;
	    }

	    // START-InferenceResult
	    public bool isPossiblyFalse()
	    {
		return !timedOut && proofs.Count == 0;
	    }

	    public bool isTrue()
	    {
		return proofs.Count > 0;
	    }

	    public bool isUnknownDueToTimeout()
	    {
		return timedOut && proofs.Count == 0;
	    }

	    public bool isPartialResultDueToTimeout()
	    {
		return timedOut && proofs.Count > 0;
	    }

	    public List getProofs()
	    {
		return proofs;
	    }

	    // END-InferenceResult

	    public bool isComplete()
	    {
		return complete;
	    }

	    public bool isLookingForAnswerLiteral()
	    {
		return !answerClause.isEmpty();
	    }

	    public bool isCheckForUnitRefutation(Clause clause)
	    {

		if (isLookingForAnswerLiteral())
		{
		    if (2 == clause.getNumberLiterals())
		    {
			foreach (Literal t in clause.getLiterals())
			{
			    if (t.getAtomicSentence().getSymbolicName().Equals(
				    answerLiteral.getAtomicSentence()
					    .getSymbolicName()))
			    {
				return true;
			    }
			}
		    }
		}
		else
		{
		    return clause.isUnitClause();
		}

		return false;
	    }

	    public bool isAnswer(Clause aClause)
	    {
		bool isAns = false;

		if (answerClause.isEmpty())
		{
		    if (aClause.isEmpty())
		    {
			proofs.Add(new ProofFinal(aClause.getProofStep(),
				new Dictionary()));
			complete = true;
			isAns = true;
		    }
		}
		else
		{
		    if (aClause.isEmpty())
		    {
			// This should not happen
			// as added an answer literal to sos, which
			// implies the database (i.e. premises) are
			// unsatisfiable to begin with.
			throw new ApplicationException(
				"Generated an empty clause while looking for an answer, implies original KB or usable is unsatisfiable");
		    }

		    if (aClause.isUnitClause()
			    && aClause.isDefiniteClause()
			    && aClause.getPositiveLiterals()[0]
				    .getAtomicSentence().getSymbolicName().Equals(
					    answerLiteral.getAtomicSentence()
						    .getSymbolicName()))
		    {
			Dictionary answerBindings = new Dictionary();
			List answerTerms = aClause.getPositiveLiterals()[
				0].getAtomicSentence().getArgs();
			int idx = 0;
			foreach (Variable v in answerLiteralVariables)
			{
			    answerBindings.Add(v, (Term)answerTerms[idx]);
			    idx++;
			}
			bool addNewAnswer = true;
			foreach (Proof p in proofs)
			{
			    if (p.getAnswerBindings().Equals(answerBindings))
			    {
				addNewAnswer = false;
				break;
			    }
			}
			if (addNewAnswer)
			{
			    proofs.Add(new ProofFinal(aClause.getProofStep(),
				    answerBindings));
			}
			isAns = true;
		    }
		}

		if (DateTime.UtcNow.Ticks > finishTime)
		{
		    complete = true;
		    // Indicate that I have run out of query time
		    timedOut = true;
		}

		return isAns;
	    }

	    public override String ToString()
	    {
		StringBuilder sb = new StringBuilder();
		sb.Append("isComplete=" + complete);
		sb.Append("\n");
		sb.Append("result=" + proofs);
		return sb.ToString();
	    }
	}
    }
}

================================================
FILE: aima-csharp/logic/fol/inference/FOLTFMResolution.cs
================================================
using System;
using System.Collections.Generic;
using System.Text;
using aima.core.logic.fol;
using aima.core.logic.fol.inference.proof;
using aima.core.logic.fol.inference.trace;
using aima.core.logic.fol.kb;
using aima.core.logic.fol.kb.data;
using aima.core.logic.fol.parsing.ast;

namespace aima.core.logic.fol.inference
{
    /**
     * Artificial Intelligence A Modern Approach (3rd Edition): page 347.
*
* The algorithmic approach is identical to the propositional case, described in * Figure 7.12.
*
* However, this implementation will use the T)wo F)inger M)ethod for looking * for resolvents between clauses, which is very inefficient.
*
* see:
* * http://logic.stanford.edu/classes/cs157/2008/lectures/lecture04.pdf, * slide 21 for the propositional case. In addition, an Answer literal will be * used so that queries with Variables may be answered (see pg. 350 of AIMA3e). * * @author Ciaran O'Reilly * */ public class FOLTFMResolution : InferenceProcedure { private long maxQueryTime = 10 * 1000; private FOLTFMResolutionTracer tracer = null; public FOLTFMResolution() { } public FOLTFMResolution(long maxQueryTime) { setMaxQueryTime(maxQueryTime); } public FOLTFMResolution(FOLTFMResolutionTracer tracer) { setTracer(tracer); } public long getMaxQueryTime() { return maxQueryTime; } public void setMaxQueryTime(long maxQueryTime) { this.maxQueryTime = maxQueryTime; } public FOLTFMResolutionTracer getTracer() { return tracer; } public void setTracer(FOLTFMResolutionTracer tracer) { this.tracer = tracer; } // START-InferenceProcedure public InferenceResult ask(FOLKnowledgeBase KB, Sentence alpha) { // clauses <- the set of clauses in CNF representation of KB ^ ~alpha List clauses = new List(); foreach (Clause c in KB.getAllClauses()) { Clause c2 = KB.standardizeApart(c); c2.setStandardizedApartCheckNotRequired(); clauses.AddRange(c2.getFactors()); } Sentence notAlpha = new NotSentence(alpha); // Want to use an answer literal to pull // query variables where necessary Literal answerLiteral = KB.createAnswerLiteral(notAlpha); List answerLiteralVariables = KB .collectAllVariables(answerLiteral.getAtomicSentence()); Clause answerClause = new Clause(); if (answerLiteralVariables.Count > 0) { Sentence notAlphaWithAnswer = new ConnectedSentence(Connectors.OR, notAlpha, answerLiteral.getAtomicSentence()); foreach (Clause c in KB.convertToClauses(notAlphaWithAnswer)) { Clause c2 = KB.standardizeApart(c); c2.setProofStep(new ProofStepGoal(c2)); c2.setStandardizedApartCheckNotRequired(); clauses.AddRange(c2.getFactors()); } answerClause.addLiteral(answerLiteral); } else { foreach (Clause c in KB.convertToClauses(notAlpha)) { Clause c2 = KB.standardizeApart(c); c2.setProofStep(new ProofStepGoal(c2)); c2.setStandardizedApartCheckNotRequired(); clauses.AddRange(c2.getFactors()); } } TFMAnswerHandler ansHandler = new TFMAnswerHandler(answerLiteral, answerLiteralVariables, answerClause, maxQueryTime); // new <- {} List newClauses = new List(); List toAdd = new List(); // loop do int noOfPrevClauses = clauses.Count; do { if (null != tracer) { tracer.stepStartWhile(new HashSet(clauses), clauses.Count, newClauses .Count); } newClauses.Clear(); // for each Ci, Cj in clauses do Clause[] clausesA = new Clause[clauses.Count]; clausesA = clauses.ToArray(); // Basically, using the simple T)wo F)inger M)ethod here. for (int i = 0; i < clausesA.Length; i++) { Clause cI = clausesA[i]; if (null != tracer) { tracer.stepOuterFor(cI); } for (int j = i; j < clausesA.Length; j++) { Clause cJ = clausesA[j]; if (null != tracer) { tracer.stepInnerFor(cI, cJ); } // resolvent <- FOL-RESOLVE(Ci, Cj) List resolvents = cI.binaryResolvents(cJ); if (resolvents.Count > 0) { toAdd.Clear(); // new <- new resolvent foreach (Clause rc in resolvents) { toAdd.AddRange(rc.getFactors()); } if (null != tracer) { tracer.stepResolved(cI, cJ, new HashSet(toAdd)); } ansHandler.checkForPossibleAnswers(toAdd); if (ansHandler.isComplete()) { break; } newClauses.AddRange(toAdd); } if (ansHandler.isComplete()) { break; } } if (ansHandler.isComplete()) { break; } } noOfPrevClauses = clauses.Count; // clauses <- clauses new clauses.AddRange(newClauses); if (ansHandler.isComplete()) { break; } // if new is a of clauses then finished // searching for an answer // (i.e. when they were added the # clauses // did not increase). } while (noOfPrevClauses < clauses.Count); if (null != tracer) { tracer.stepFinished(new HashSet(clauses), ansHandler); } return ansHandler; } // END-InferenceProcedure // // // PRIVATE METHODS // class TFMAnswerHandler : InferenceResult { private Literal answerLiteral = null; private List answerLiteralVariables = null; private Clause answerClause = null; private long finishTime = 0L; private bool complete = false; private List proofs = new List(); private bool timedOut = false; public TFMAnswerHandler(Literal answerLiteral, List answerLiteralVariables, Clause answerClause, long maxQueryTime) { this.answerLiteral = answerLiteral; this.answerLiteralVariables = answerLiteralVariables; this.answerClause = answerClause; // this.finishTime = DateTime.UtcNow.Ticks + maxQueryTime; } // // START-InferenceResult public bool isPossiblyFalse() { return !timedOut && proofs.Count == 0; } public bool isTrue() { return proofs.Count > 0; } public bool isUnknownDueToTimeout() { return timedOut && proofs.Count == 0; } public bool isPartialResultDueToTimeout() { return timedOut && proofs.Count > 0; } public List getProofs() { return proofs; } // END-InferenceResult public bool isComplete() { return complete; } public void checkForPossibleAnswers(List resolvents) { // If no bindings being looked for, then // is just a true false query. foreach (Clause aClause in resolvents) { if (answerClause.isEmpty()) { if (aClause.isEmpty()) { proofs.Add(new ProofFinal(aClause.getProofStep(), new Dictionary())); complete = true; } } else { if (aClause.isEmpty()) { // This should not happen // as added an answer literal, which // implies the database (i.e. premises) are // unsatisfiable to begin with. throw new ApplicationException( "Generated an empty clause while looking for an answer, implies original KB is unsatisfiable"); } if (aClause.isUnitClause() && aClause.isDefiniteClause() && aClause.getPositiveLiterals()[0] .getAtomicSentence().getSymbolicName() .Equals( answerLiteral.getAtomicSentence() .getSymbolicName())) { Dictionary answerBindings = new Dictionary(); List answerTerms = aClause.getPositiveLiterals() [0].getAtomicSentence().getArgs(); int idx = 0; foreach (Variable v in answerLiteralVariables) { answerBindings.Add(v, (Term)answerTerms[idx]); idx++; } bool addNewAnswer = true; foreach (Proof p in proofs) { if (p.getAnswerBindings().Equals(answerBindings)) { addNewAnswer = false; break; } } if (addNewAnswer) { proofs.Add(new ProofFinal(aClause.getProofStep(), answerBindings)); } } } if (DateTime.UtcNow.Ticks > finishTime) { complete = true; // Indicate that I have run out of query time timedOut = true; } } } public override String ToString() { StringBuilder sb = new StringBuilder(); sb.Append("isComplete=" + complete); sb.Append("\n"); sb.Append("result=" + proofs); return sb.ToString(); } } } } ================================================ FILE: aima-csharp/logic/fol/inference/InferenceProcedure.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.kb; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference { /** * @author Ciaran O'Reilly * */ public interface InferenceProcedure { /** * * @param kb * the knowledge base against which the query is to be made. * @param query * the query to be answered. * @return an InferenceResult. */ InferenceResult ask(FOLKnowledgeBase kb, Sentence query); } } ================================================ FILE: aima-csharp/logic/fol/inference/InferenceResult.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.inference.proof; namespace aima.core.logic.fol.inference { /** * @author Ciaran O'Reilly * */ public interface InferenceResult { /** * * @return true, if the query is not entailed from the premises. This just * means the query is not entailed, the query itself may be true. */ bool isPossiblyFalse(); /** * * @return true, if the query is entailed from the premises (Note: can get * partial results if the original query contains variables * indicating that there can possibly be more than 1 proof/bindings * for the query, see: isPartialResultDueToTimeout()). */ bool isTrue(); /** * * @return true, if the inference procedure ran for a length of time and * found no proof one way or the other before it timed out. */ bool isUnknownDueToTimeout(); /** * * @return true, if the inference procedure found a proof for a query * containing variables (i.e. possibly more than 1 proof can be * returned) and the inference procedure was still looking for other * possible answers before it timed out. */ bool isPartialResultDueToTimeout(); /** * * @return a list of 0 or more proofs (multiple proofs can be returned if * the original query contains variables). */ List getProofs(); } } ================================================ FILE: aima-csharp/logic/fol/inference/InferenceResultPrinter.cs ================================================ using System; using System.Collections.Generic; using System.Text; using aima.core.logic.fol.inference.proof; namespace aima.core.logic.fol.inference { /** * @author Ciaran O'Reilly * */ public class InferenceResultPrinter { /** * Utility method for outputting InferenceResults in a formatted textual * representation. * * @param ir * an InferenceResult * @return a String representation of the InferenceResult. */ public static String printInferenceResult(InferenceResult ir) { StringBuilder sb = new StringBuilder(); sb.Append("InferenceResult.isTrue=" + ir.isTrue()); sb.Append("\n"); sb.Append("InferenceResult.isPossiblyFalse=" + ir.isPossiblyFalse()); sb.Append("\n"); sb.Append("InferenceResult.isUnknownDueToTimeout=" + ir.isUnknownDueToTimeout()); sb.Append("\n"); sb.Append("InferenceResult.isPartialResultDueToTimeout=" + ir.isPartialResultDueToTimeout()); sb.Append("\n"); sb.Append("InferenceResult.#Proofs=" + ir.getProofs().Count); sb.Append("\n"); int proofNo = 0; List proofs = ir.getProofs(); foreach (Proof p in proofs) { proofNo++; sb.Append("InferenceResult.Proof#" + proofNo + "=\n" + ProofPrinter.printProof(p)); } return sb.ToString(); } } } ================================================ FILE: aima-csharp/logic/fol/inference/Paramodulation.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol; using aima.core.logic.fol.inference.proof; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference { /** * Artificial Intelligence A Modern Approach (3r Edition): page 354.
*
* Paramodulation: For any terms x, y, and z, where z appears somewhere * in literal mi, and where UNIFY(x,z) = θ,
* *
     *                          l1 OR ... OR lk OR x=y,     m1 OR ... OR mn
     *     ------------------------------------------------------------------------
     *     SUB(SUBST(θ,x), SUBST(θ,y), SUBST(θ, l1 OR ... OR lk OR m1 OR ... OR mn))
     * 
* * Paramodulation yields a complete inference procedure for first-order logic * with equality. * * @author Ciaran O'Reilly * */ public class Paramodulation : AbstractModulation { private static StandardizeApartIndexical _saIndexical = StandardizeApartIndexicalFactory .newStandardizeApartIndexical('p'); private static List _emptyLiteralList = new List(); private StandardizeApart sApart = new StandardizeApart(); public Paramodulation() { } public List apply(Clause c1, Clause c2) { return apply(c1, c2, false); } public List apply(Clause c1, Clause c2, bool standardizeApart) { List paraExpressions = new List(); for (int i = 0; i < 2; i++) { Clause topClause, equalityClause; if (i == 0) { topClause = c1; equalityClause = c2; } else { topClause = c2; equalityClause = c1; } foreach (Literal possEqLit in equalityClause.getLiterals()) { // Must be a positive term equality to be used // for paramodulation. if (possEqLit.isPositiveLiteral() && possEqLit.getAtomicSentence() is TermEquality) { TermEquality assertion = (TermEquality)possEqLit .getAtomicSentence(); // Test matching for both sides of the equality for (int x = 0; x < 2; x++) { Term toMatch, toReplaceWith; if (x == 0) { toMatch = assertion.getTerm1(); toReplaceWith = assertion.getTerm2(); } else { toMatch = assertion.getTerm2(); toReplaceWith = assertion.getTerm1(); } foreach (Literal l1 in topClause.getLiterals()) { IdentifyCandidateMatchingTerm icm = getMatchingSubstitution( toMatch, l1.getAtomicSentence()); if (null != icm) { Term replaceWith = substVisitor.subst(icm .getMatchingSubstitution(), toReplaceWith); // Want to ignore reflexivity axiom situation, // i.e. x = x if (icm.getMatchingTerm().Equals(replaceWith)) { continue; } ReplaceMatchingTerm rmt = new ReplaceMatchingTerm(); AtomicSentence altExpression = rmt.replace(l1 .getAtomicSentence(), icm .getMatchingTerm(), replaceWith); // I have an alternative, create a new clause // with the alternative and the substitution // applied to all the literals before returning List newLits = new List(); foreach (Literal l2 in topClause.getLiterals()) { if (l1.Equals(l2)) { newLits .Add(l1 .newInstance((AtomicSentence)substVisitor .subst( icm .getMatchingSubstitution(), altExpression))); } else { newLits .Add(substVisitor .subst( icm .getMatchingSubstitution(), l2)); } } // Assign the equality clause literals, // excluding // the term equality used. foreach (Literal l2 in equalityClause.getLiterals()) { if (possEqLit.Equals(l2)) { continue; } newLits.Add(substVisitor.subst(icm .getMatchingSubstitution(), l2)); } // Only apply paramodulation at most once // for each term equality. Clause nc = null; if (standardizeApart) { sApart.standardizeApart(newLits, _emptyLiteralList, _saIndexical); nc = new Clause(newLits); } else { nc = new Clause(newLits); } nc .setProofStep(new ProofStepClauseParamodulation( nc, topClause, equalityClause, assertion)); if (c1.isImmutable()) { nc.setImmutable(); } if (!c1.isStandardizedApartCheckRequired()) { c1.setStandardizedApartCheckNotRequired(); } paraExpressions.Add(nc); break; } } } } } } return paraExpressions; } // PROTECTED METHODS public override bool isValidMatch(Term toMatch, List toMatchVariables, Term possibleMatch, Dictionary substitution) { if (possibleMatch != null && substitution != null) { // Note: // [Brand 1975] showed that paramodulation into // variables is unnecessary. if (!(possibleMatch is Variable)) { // TODO: Find out whether the following statement from: // http://www.cs.miami.edu/~geoff/Courses/CSC648-07F/Content/ // Paramodulation.shtml // is actually the case, as it was not positive but // intuitively makes sense: // "Similarly, depending on how paramodulation is used, it is // often unnecessary to paramodulate from variables." // if (!(toMatch is Variable)) { return true; // } } } return false; } } } ================================================ FILE: aima-csharp/logic/fol/inference/otter/ClauseFilter.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.otter { /** * @author Ciaran O'Reilly * */ public interface ClauseFilter { HashSet filter(HashSet clauses); } } ================================================ FILE: aima-csharp/logic/fol/inference/otter/ClauseSimplifier.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.otter { /** * @author Ciaran O'Reilly * */ public interface ClauseSimplifier { Clause simplify(Clause c); } } ================================================ FILE: aima-csharp/logic/fol/inference/otter/LightestClauseHeuristic.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.otter { /** * Heuristic for selecting lightest clause from SOS. To avoid recalculating the * lightest clause on every call, the interface supports defining the initial * sos and updates to that set so that it can maintain its own internal data * structures to allow for incremental re-calculation of the lightest clause. * * @author Ciaran O'Reilly * */ public interface LightestClauseHeuristic { /** * Get the lightest clause from the SOS * * @return the lightest clause. */ Clause getLightestClause(); /** * SOS life-cycle methods allowing implementations of this interface to * incrementally update the calculation of the lightest clause as opposed to * having to recalculate each time. * * @param clauses */ void initialSOS(List clauses); void addedClauseToSOS(Clause clause); void removedClauseFromSOS(Clause clause); } } ================================================ FILE: aima-csharp/logic/fol/inference/otter/defaultimpl/DefaultClauseFilter.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.inference.otter; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.otter.defaultimpl { /** * @author Ciaran O'Reilly * */ public class DefaultClauseFilter : ClauseFilter { public DefaultClauseFilter() { } // START-ClauseFilter public HashSet filter(HashSet clauses) { return clauses; } // END-ClauseFilter } } ================================================ FILE: aima-csharp/logic/fol/inference/otter/defaultimpl/DefaultClauseSimplifier.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using aima.core.logic.fol.inference; using aima.core.logic.fol.inference.otter; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.otter.defaultimpl { /** * @author Ciaran O'Reilly * */ public class DefaultClauseSimplifier : ClauseSimplifier { private Demodulation demodulation = new Demodulation(); private List rewrites = new List(); public DefaultClauseSimplifier() { } public DefaultClauseSimplifier(List rewrites) { this.rewrites.AddRange(rewrites); } // START-ClauseSimplifier public Clause simplify(Clause c) { Clause simplified = c; // Apply each of the rewrite rules to // the clause foreach (TermEquality te in rewrites) { Clause dc = simplified; // Keep applying the rewrite as many times as it // can be applied before moving on to the next one. while (null != (dc = demodulation.apply(te, dc))) { simplified = dc; } } return simplified; } // END-ClauseSimplifier } } ================================================ FILE: aima-csharp/logic/fol/inference/otter/defaultimpl/DefaultLightestClauseHeuristic.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using aima.core.logic.fol.inference.otter; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.otter.defaultimpl { /** * @author Ciaran O'Reilly * */ public class DefaultLightestClauseHeuristic : LightestClauseHeuristic { private SortedSet sos; public DefaultLightestClauseHeuristic() { LightestClauseSorter c = new LightestClauseSorter(); sos = new SortedSet(c); } // START-LightestClauseHeuristic public Clause getLightestClause() { Clause lightest = null; if (sos.Count > 0) { lightest = sos.First(); } return lightest; } public void initialSOS(List clauses) { sos.Clear(); sos.UnionWith(clauses); } public void addedClauseToSOS(Clause clause) { sos.Add(clause); } public void removedClauseFromSOS(Clause clause) { sos.Remove(clause); } // END-LightestClauseHeuristic } class LightestClauseSorter : IComparer { public int Compare(Clause c1, Clause c2) { if (c1.Equals(c2)) { return 0; } int c1Val = c1.getNumberLiterals(); int c2Val = c2.getNumberLiterals(); return (c1Val < c2Val ? -1 : (c1Val == c2Val ? (compareEqualityIdentities(c1, c2)) : 1)); } private int compareEqualityIdentities(Clause c1, Clause c2) { int c1Len = c1.getEqualityIdentity().Length; int c2Len = c2.getEqualityIdentity().Length; return (c1Len < c2Len ? -1 : (c1Len == c2Len ? c1.getEqualityIdentity() .CompareTo(c2.getEqualityIdentity()) : 1)); } } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/AbstractProofStep.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public abstract class AbstractProofStep : ProofStep { private int step = 0; public AbstractProofStep() { } // START-ProofStep public int getStepNumber() { return step; } public void setStepNumber(int step) { this.step = step; } public abstract List getPredecessorSteps(); public abstract String getProof(); public abstract String getJustification(); // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/Proof.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public interface Proof { /** * * @return A list of proof steps that show how an answer was derived. */ List getSteps(); /** * * @return a Map of bindings for any variables that were in the original * query. Will be an empty Map if no variables existed in the * original query. */ Dictionary getAnswerBindings(); /** * * @param updatedBindings * allows for the bindings to be renamed. Note: should not be * used for any other reason. */ void replaceAnswerBindings(Dictionary updatedBindings); } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofFinal.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofFinal : Proof { private Dictionary answerBindings = new Dictionary(); private ProofStep finalStep = null; private List proofSteps = null; public ProofFinal(ProofStep finalStep, Dictionary answerBindings) { this.finalStep = finalStep; this.answerBindings = answerBindings; } // START-Proof public List getSteps() { // Only calculate if the proof steps are actually requested. if (null == proofSteps) { calculateProofSteps(); } return proofSteps; } public Dictionary getAnswerBindings() { return answerBindings; } public void replaceAnswerBindings(Dictionary updatedBindings) { answerBindings.Clear(); answerBindings = updatedBindings; } // END-Proof public override String ToString() { return answerBindings.ToString(); } // PRIVATE METHODS private void calculateProofSteps() { proofSteps = new List(); addToProofSteps(finalStep); // Move all premises to the front of the // list of steps int to = 0; for (int i = 0; i < proofSteps.Count; i++) { if (proofSteps[i] is ProofStepPremise) { ProofStep m = proofSteps[i]; proofSteps.RemoveAt(i); proofSteps.Insert(to, m); to++; } } // Move the Goals after the premises for (int i = 0; i < proofSteps.Count; i++) { if (proofSteps[i] is ProofStepGoal) { ProofStep m = proofSteps[i]; proofSteps.RemoveAt(i); proofSteps.Insert(to, m); to++; } } // Assign the step #s now that all the proof // steps have been unwound for (int i = 0; i < proofSteps.Count; i++) { proofSteps[i].setStepNumber(i + 1); } } private void addToProofSteps(ProofStep step) { if (!proofSteps.Contains(step)) { proofSteps.Insert(0, step); } else { proofSteps.Remove(step); proofSteps.Insert(0, step); } List predecessors = step.getPredecessorSteps(); for (int i = predecessors.Count - 1; i >= 0; i--) { addToProofSteps(predecessors[i]); } } } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofPrinter.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofPrinter { /** * Utility method for outputting proofs in a formatted textual * representation. * * @param proof * @return a String representation of the Proof. */ public static String printProof(Proof proof) { StringBuilder sb = new StringBuilder(); sb.Append("Proof, Answer Bindings: "); sb.Append(proof.getAnswerBindings()); sb.Append("\n"); List steps = proof.getSteps(); int maxStepWidth = "Step".Length; int maxProofWidth = "Proof".Length; int maxJustificationWidth = "Justification".Length; // Calculate the maximum width for each column in the proof foreach (ProofStep step in steps) { String sn = "" + step.getStepNumber(); if (sn.Length > maxStepWidth) { maxStepWidth = sn.Length; } if (step.getProof().Length > maxProofWidth) { maxProofWidth = step.getProof().Length; } if (step.getJustification().Length > maxJustificationWidth) { maxJustificationWidth = step.getJustification().Length; } } // Give a little extra padding maxStepWidth += 1; maxProofWidth += 1; maxJustificationWidth += 1; String f = "|%-" + maxStepWidth + "s| %-" + maxProofWidth + "s|%-" + maxJustificationWidth + "s|\n"; int barWidth = 5 + maxStepWidth + maxProofWidth + maxJustificationWidth; StringBuilder bar = new StringBuilder(); for (int i = 0; i < barWidth; i++) { bar.Append("-"); } bar.Append("\n"); sb.Append(bar); sb.Append(String.Format(f, "Step", "Proof", "Justification")); sb.Append(bar); foreach (ProofStep step in steps) { sb.Append(String.Format(f, "" + step.getStepNumber(), step .getProof(), step.getJustification())); } sb.Append(bar); return sb.ToString(); } } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStep.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public interface ProofStep { int getStepNumber(); void setStepNumber(int step); List getPredecessorSteps(); String getProof(); String getJustification(); } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepBwChGoal.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepBwChGoal : AbstractProofStep { private List predecessors = new List(); private Clause toProve = null; private Literal currentGoal = null; private Dictionary bindings = new Dictionary(); public ProofStepBwChGoal(Clause toProve, Literal currentGoal, Dictionary bindings) { this.toProve = toProve; this.currentGoal = currentGoal; foreach (Variable key in bindings.Keys) { this.bindings.Add(key, bindings[key]); } } public Dictionary getBindings() { return bindings; } public void setPredecessor(ProofStep predecessor) { predecessors.Clear(); predecessors.Add(predecessor); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { StringBuilder sb = new StringBuilder(); List nLits = toProve.getNegativeLiterals(); for (int i = 0; i < toProve.getNumberNegativeLiterals(); i++) { sb.Append(nLits[i].getAtomicSentence()); if (i != (toProve.getNumberNegativeLiterals() - 1)) { sb.Append(" AND "); } } if (toProve.getNumberNegativeLiterals() > 0) { sb.Append(" => "); } sb.Append(toProve.getPositiveLiterals()[0]); return sb.ToString(); } public override String getJustification() { return "Current Goal " + currentGoal.getAtomicSentence().ToString() + ", " + bindings; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepChainCancellation.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepChainCancellation : AbstractProofStep { private List predecessors = new List(); private Chain cancellation = null; private Chain cancellationOf = null; private Dictionary subst = null; public ProofStepChainCancellation(Chain cancellation, Chain cancellationOf, Dictionary subst) { this.cancellation = cancellation; this.cancellationOf = cancellationOf; this.subst = subst; this.predecessors.Add(cancellationOf.getProofStep()); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return cancellation.ToString(); } public override String getJustification() { return "Cancellation: " + cancellationOf.getProofStep().getStepNumber() + " " + subst; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepChainContrapositive.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepChainContrapositive : AbstractProofStep { private List predecessors = new List(); private Chain contrapositive = null; private Chain contrapositiveOf = null; public ProofStepChainContrapositive(Chain contrapositive, Chain contrapositiveOf) { this.contrapositive = contrapositive; this.contrapositiveOf = contrapositiveOf; this.predecessors.Add(contrapositiveOf.getProofStep()); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return contrapositive.ToString(); } public override String getJustification() { return "Contrapositive: " + contrapositiveOf.getProofStep().getStepNumber(); } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepChainDropped.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepChainDropped : AbstractProofStep { private List predecessors = new List(); private Chain dropped = null; private Chain droppedOff = null; public ProofStepChainDropped(Chain dropped, Chain droppedOff) { this.dropped = dropped; this.droppedOff = droppedOff; this.predecessors.Add(droppedOff.getProofStep()); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return dropped.ToString(); } public override String getJustification() { return "Dropped: " + droppedOff.getProofStep().getStepNumber(); } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepChainFromClause.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepChainFromClause : AbstractProofStep { private List predecessors = new List(); private Chain chain = null; private Clause fromClause = null; public ProofStepChainFromClause(Chain chain, Clause fromClause) { this.chain = chain; this.fromClause = fromClause; this.predecessors.Add(fromClause.getProofStep()); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return chain.ToString(); } public override String getJustification() { return "Chain from Clause: " + fromClause.getProofStep().getStepNumber(); } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepChainReduction.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepChainReduction : AbstractProofStep { private List predecessors = new List(); private Chain reduction = null; private Chain nearParent, farParent = null; private Dictionary subst = null; public ProofStepChainReduction(Chain reduction, Chain nearParent, Chain farParent, Dictionary subst) { this.reduction = reduction; this.nearParent = nearParent; this.farParent = farParent; this.subst = subst; this.predecessors.Add(farParent.getProofStep()); this.predecessors.Add(nearParent.getProofStep()); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return reduction.ToString(); } public override String getJustification() { return "Reduction: " + nearParent.getProofStep().getStepNumber() + "," + farParent.getProofStep().getStepNumber() + " " + subst; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepClauseBinaryResolvent.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepClauseBinaryResolvent : AbstractProofStep { private List predecessors = new List(); private Clause resolvent = null; private Literal posLiteral = null; private Literal negLiteral = null; private Clause parent1, parent2 = null; private Dictionary subst = new Dictionary(); private Dictionary renameSubst = new Dictionary(); private Clause c; private Clause clause; private Clause othC; private Dictionary copyRBindings; private Dictionary renameSubstitituon; public ProofStepClauseBinaryResolvent(Clause resolvent, Literal pl, Literal nl, Clause parent1, Clause parent2, Dictionary subst, Dictionary renameSubst) { this.resolvent = resolvent; this.posLiteral = pl; this.negLiteral = nl; this.parent1 = parent1; this.parent2 = parent2; foreach (Variable key in subst.Keys) { this.subst.Add(key, subst[key]); } foreach (Variable key in renameSubst.Keys) { this.renameSubst.Add(key, renameSubst[key]); } this.predecessors.Add(parent1.getProofStep()); this.predecessors.Add(parent2.getProofStep()); } public ProofStepClauseBinaryResolvent(Clause c, Clause clause, Clause othC, Dictionary copyRBindings, Dictionary renameSubstitituon) { this.c = c; this.clause = clause; this.othC = othC; this.copyRBindings = copyRBindings; this.renameSubstitituon = renameSubstitituon; } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return resolvent.ToString(); } public override String getJustification() { int lowStep = parent1.getProofStep().getStepNumber(); int highStep = parent2.getProofStep().getStepNumber(); if (lowStep > highStep) { lowStep = highStep; highStep = parent1.getProofStep().getStepNumber(); } return "Resolution: " + lowStep + ", " + highStep + " [" + posLiteral + ", " + negLiteral + "], subst=" + subst + ", renaming=" + renameSubst; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepClauseClausifySentence.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepClauseClausifySentence : AbstractProofStep { private List predecessors = new List(); private Clause clausified = null; public ProofStepClauseClausifySentence(Clause clausified, Sentence origSentence) { this.clausified = clausified; this.predecessors.Add(new ProofStepPremise(origSentence)); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return clausified.ToString(); } public override String getJustification() { return "Clausified " + predecessors[0].getStepNumber(); } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepClauseDemodulation.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepClauseDemodulation : AbstractProofStep { private List predecessors = new List(); private Clause demodulated = null; private Clause origClause = null; private TermEquality assertion = null; public ProofStepClauseDemodulation(Clause demodulated, Clause origClause, TermEquality assertion) { this.demodulated = demodulated; this.origClause = origClause; this.assertion = assertion; this.predecessors.Add(origClause.getProofStep()); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return demodulated.ToString(); } public override String getJustification() { return "Demodulation: " + origClause.getProofStep().getStepNumber() + ", [" + assertion + "]"; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepClauseFactor.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepClauseFactor : AbstractProofStep { private List predecessors = new List(); private Clause factor = null; private Clause factorOf = null; private Literal lx = null; private Literal ly = null; private Dictionary subst = new Dictionary(); private Dictionary renameSubst = new Dictionary(); public ProofStepClauseFactor(Clause factor, Clause factorOf, Literal lx, Literal ly, Dictionary subst, Dictionary renameSubst) { this.factor = factor; this.factorOf = factorOf; this.lx = lx; this.ly = ly; foreach (Variable key in subst.Keys) { this.subst.Add(key, subst[key]); } foreach (Variable key in renameSubst.Keys) { this.renameSubst.Add(key, renameSubst[key]); } this.predecessors.Add(factorOf.getProofStep()); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return factor.ToString(); } public override String getJustification() { return "Factor of " + factorOf.getProofStep().getStepNumber() + " [" + lx + ", " + ly + "], subst=" + subst + ", renaming=" + renameSubst; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepClauseParamodulation.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepClauseParamodulation : AbstractProofStep { private List predecessors = new List(); private Clause paramodulated = null; private Clause topClause = null; private Clause equalityClause = null; private TermEquality assertion = null; public ProofStepClauseParamodulation(Clause paramodulated, Clause topClause, Clause equalityClause, TermEquality assertion) { this.paramodulated = paramodulated; this.topClause = topClause; this.equalityClause = equalityClause; this.assertion = assertion; this.predecessors.Add(topClause.getProofStep()); this.predecessors.Add(equalityClause.getProofStep()); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return paramodulated.ToString(); } public override String getJustification() { return "Paramodulation: " + topClause.getProofStep().getStepNumber() + ", " + equalityClause.getProofStep().getStepNumber() + ", [" + assertion + "]"; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepFoChAlreadyAFact.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepFoChAlreadyAFact : AbstractProofStep { private readonly List _noPredecessors = new List(); private Literal fact = null; public ProofStepFoChAlreadyAFact(Literal fact) { this.fact = fact; } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(_noPredecessors).ToList(); } public override String getProof() { return fact.ToString(); } public override String getJustification() { return "Already a known fact in the KB."; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepFoChAssertFact.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepFoChAssertFact : AbstractProofStep { private List predecessors = new List(); private Clause implication = null; private Literal fact = null; private Dictionary bindings = null; public ProofStepFoChAssertFact(Clause implication, Literal fact, Dictionary bindings, ProofStep predecessor) { this.implication = implication; this.fact = fact; this.bindings = bindings; if (null != predecessor) { predecessors.Add(predecessor); } } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { StringBuilder sb = new StringBuilder(); List nLits = implication.getNegativeLiterals(); for (int i = 0; i < implication.getNumberNegativeLiterals(); i++) { sb.Append(nLits[i].getAtomicSentence()); if (i != (implication.getNumberNegativeLiterals() - 1)) { sb.Append(" AND "); } } sb.Append(" => "); sb.Append(implication.getPositiveLiterals()[0]); return sb.ToString(); } public override String getJustification() { return "Assert fact " + fact.ToString() + ", " + bindings; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepGoal.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepGoal : AbstractProofStep { private static readonly List _noPredecessors = new List(); private Object proof = ""; public ProofStepGoal(Object proof) { this.proof = proof; } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(_noPredecessors).ToList(); } public override String getProof() { return proof.ToString(); } public override String getJustification() { return "Goal"; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepPremise.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepPremise : AbstractProofStep { private static readonly List _noPredecessors = new List(); private Object proof = ""; public ProofStepPremise(Object proof) { this.proof = proof; } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(_noPredecessors).ToList(); } public override String getProof() { return proof.ToString(); } public override String getJustification() { return "Premise"; } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/proof/ProofStepRenaming.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; namespace aima.core.logic.fol.inference.proof { /** * @author Ciaran O'Reilly * */ public class ProofStepRenaming : AbstractProofStep { private List predecessors = new List(); private Object proof = ""; public ProofStepRenaming(Object proof, ProofStep predecessor) { this.proof = proof; this.predecessors.Add(predecessor); } // START-ProofStep public override List getPredecessorSteps() { return new ReadOnlyCollection(predecessors).ToList(); } public override String getProof() { return proof.ToString(); } public override String getJustification() { return "Renaming of " + predecessors[0].getStepNumber(); } // END-ProofStep } } ================================================ FILE: aima-csharp/logic/fol/inference/trace/FOLModelEliminationTracer.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.logic.fol.inference.trace { /** * @author Ciaran O'Reilly * */ public interface FOLModelEliminationTracer { void reset(); void increment(int depth, int noFarParents); } } ================================================ FILE: aima-csharp/logic/fol/inference/trace/FOLTFMResolutiontracer.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.inference; using aima.core.logic.fol.kb.data; namespace aima.core.logic.fol.inference.trace { /** * @author Ciaran O'Reilly * */ public interface FOLTFMResolutionTracer { void stepStartWhile(HashSet clauses, int totalNoClauses, int totalNoNewCandidateClauses); void stepOuterFor(Clause i); void stepInnerFor(Clause i, Clause j); void stepResolved(Clause iFactor, Clause jFactor, HashSet resolvents); void stepFinished(HashSet clauses, InferenceResult result); } } ================================================ FILE: aima-csharp/logic/fol/kb/FOLKnowledgeBase.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using aima.core.logic.fol; using aima.core.logic.fol.inference; using aima.core.logic.fol.kb.data; using aima.core.logic.fol.parsing; using aima.core.logic.fol.parsing.ast; using aima.core.logic.fol.domain; using aima.core.logic.fol.inference.proof; namespace aima.core.logic.fol.kb { /** * A First Order Logic (FOL) Knowledge Base. * * @author Ciaran O'Reilly * */ public class FOLKnowledgeBase { private FOLParser parser; private InferenceProcedure inferenceProcedure; private Unifier unifier; private SubstVisitor substVisitor; private VariableCollector variableCollector; private StandardizeApart _standardizeApart; private CNFConverter cnfConverter; // Persistent data structures // Keeps track of the Sentences in their original form as added to the // Knowledge base. private List originalSentences = new List(); // The KB in clause form private List clauses = new List(); // Keep track of all of the definite clauses in the database // along with those that represent implications. private List allDefiniteClauses = new List(); private List implicationDefiniteClauses = new List(); // All the facts in the KB indexed by Atomic Sentence name (Note: pg. 279) private Dictionary> indexFacts = new Dictionary>(); // Keep track of indexical keys for uniquely standardizing apart sentences private StandardizeApartIndexical variableIndexical = StandardizeApartIndexicalFactory .newStandardizeApartIndexical('v'); private StandardizeApartIndexical queryIndexical = StandardizeApartIndexicalFactory .newStandardizeApartIndexical('q'); // PUBLIC METHODS public FOLKnowledgeBase(FOLDomain domain) : this(domain, new FOLOTTERLikeTheoremProver()) { // Default to Full Resolution if not set. } public FOLKnowledgeBase(FOLDomain domain, InferenceProcedure inferenceProcedure) : this(domain, inferenceProcedure, new Unifier()) { } public FOLKnowledgeBase(FOLDomain domain, InferenceProcedure inferenceProcedure, Unifier unifier) { this.parser = new FOLParser(new FOLDomain(domain)); this.inferenceProcedure = inferenceProcedure; this.unifier = unifier; // this.substVisitor = new SubstVisitor(); this.variableCollector = new VariableCollector(); this._standardizeApart = new StandardizeApart(variableCollector, substVisitor); this.cnfConverter = new CNFConverter(parser); } public void clear() { this.originalSentences.Clear(); this.clauses.Clear(); this.allDefiniteClauses.Clear(); this.implicationDefiniteClauses.Clear(); this.indexFacts.Clear(); } public InferenceProcedure getInferenceProcedure() { return inferenceProcedure; } public void setInferenceProcedure(InferenceProcedure inferenceProcedure) { if (null != inferenceProcedure) { this.inferenceProcedure = inferenceProcedure; } } public Sentence tell(String aSentence) { Sentence s = parser.parse(aSentence); tell(s); return s; } public void tell(List sentences) { foreach (Sentence s in sentences) { tell(s); } } public void tell(Sentence aSentence) { store(aSentence); } /** * * @param aQuerySentence * @return an InferenceResult. */ public InferenceResult ask(String aQuerySentence) { return ask(parser.parse(aQuerySentence)); } public InferenceResult ask(Sentence aQuery) { // Want to standardize apart the query to ensure // it does not clash with any of the sentences // in the database StandardizeApartResult saResult = _standardizeApart.standardizeApart( aQuery, queryIndexical); // Need to map the result variables (as they are standardized apart) // to the original queries variables so that the caller can easily // understand and use the returned set of substitutions InferenceResult infResult = getInferenceProcedure().ask(this, saResult.getStandardized()); List proofs = infResult.getProofs(); foreach (Proof p in proofs) { Dictionary im = p.getAnswerBindings(); Dictionary em = new Dictionary(); foreach (Variable rev in saResult.getReverseSubstitution().Keys) { em.Add((Variable)saResult.getReverseSubstitution()[rev], im[rev]); } p.replaceAnswerBindings(em); } return infResult; } public int getNumberFacts() { return allDefiniteClauses.Count - implicationDefiniteClauses.Count; } public int getNumberRules() { return clauses.Count - getNumberFacts(); } public List getOriginalSentences() { return new System.Collections.ObjectModel.ReadOnlyCollection(originalSentences).ToList(); } public List getAllDefiniteClauses() { return new System.Collections.ObjectModel.ReadOnlyCollection(allDefiniteClauses).ToList(); } public List getAllDefiniteClauseImplications() { return new System.Collections.ObjectModel.ReadOnlyCollection(implicationDefiniteClauses).ToList(); } public List getAllClauses() { return new System.Collections.ObjectModel.ReadOnlyCollection(clauses).ToList(); } // Note: pg 278, FETCH(q) concept. public /* lock */ List> fetch(Literal l) { // Get all of the substitutions in the KB that p unifies with List> allUnifiers = new List>(); List matchingFacts = fetchMatchingFacts(l); if (null != matchingFacts) { foreach (Literal fact in matchingFacts) { Dictionary substitution = unifier.unify(l .getAtomicSentence(), fact.getAtomicSentence()); if (null != substitution) { allUnifiers.Add(substitution); } } } return allUnifiers; } // Note: To support FOL-FC-Ask public List> fetch(List literals) { List> possibleSubstitutions = new List>(); if (literals.Count > 0) { Literal first = literals[0]; List rest = literals.Skip(1).ToList(); recursiveFetch(new Dictionary(), first, rest, possibleSubstitutions); } return possibleSubstitutions; } public Dictionary unify(FOLNode x, FOLNode y) { return unifier.unify(x, y); } public Sentence subst(Dictionary theta, Sentence aSentence) { return substVisitor.subst(theta, aSentence); } public Literal subst(Dictionary theta, Literal l) { return substVisitor.subst(theta, l); } public Term subst(Dictionary theta, Term aTerm) { return substVisitor.subst(theta, aTerm); } // Note: see page 277. public Sentence standardizeApart(Sentence aSentence) { return _standardizeApart.standardizeApart(aSentence, variableIndexical) .getStandardized(); } public Clause standardizeApart(Clause aClause) { return _standardizeApart.standardizeApart(aClause, variableIndexical); } public Chain standardizeApart(Chain aChain) { return _standardizeApart.standardizeApart(aChain, variableIndexical); } public List collectAllVariables(Sentence aSentence) { return variableCollector.collectAllVariables(aSentence); } public CNF convertToCNF(Sentence aSentence) { return cnfConverter.convertToCNF(aSentence); } public List convertToClauses(Sentence aSentence) { CNF cnf = cnfConverter.convertToCNF(aSentence); return new List(cnf.getConjunctionOfClauses()); } public Literal createAnswerLiteral(Sentence forQuery) { String alName = parser.getFOLDomain().addAnswerLiteral(); List terms = new List(); List vars = variableCollector.collectAllVariables(forQuery); foreach (Variable v in vars) { // Ensure copies of the variables are used. terms.Add((Term)v.copy()); } return new Literal(new Predicate(alName, terms)); } // Note: see pg. 281 public bool isRenaming(Literal l) { List possibleMatches = fetchMatchingFacts(l); if (null != possibleMatches) { return isRenaming(l, possibleMatches); } return false; } // Note: see pg. 281 public bool isRenaming(Literal l, List possibleMatches) { foreach (Literal q in possibleMatches) { if (l.isPositiveLiteral() != q.isPositiveLiteral()) { continue; } Dictionary subst = unifier.unify(l.getAtomicSentence(), q .getAtomicSentence()); if (null != subst) { int cntVarTerms = 0; foreach (Term t in subst.Values) { if (t is Variable) { cntVarTerms++; } } // If all the substitutions, even if none, map to Variables // then this is a renaming if (subst.Count == cntVarTerms) { return true; } } } return false; } public override String ToString() { StringBuilder sb = new StringBuilder(); foreach (Sentence s in originalSentences) { sb.Append(s.ToString()); sb.Append("\n"); } return sb.ToString(); } // PROTECTED METHODS protected FOLParser getParser() { return parser; } // PRIVATE METHODS // Note: pg 278, STORE(s) concept. private /*lock*/ void store(Sentence aSentence) { originalSentences.Add(aSentence); // Convert the sentence to CNF CNF cnfOfOrig = cnfConverter.convertToCNF(aSentence); List conjunctionOfClauses = cnfOfOrig.getConjunctionOfClauses(); foreach (Clause c in conjunctionOfClauses) { c.setProofStep(new ProofStepClauseClausifySentence(c, aSentence)); if (c.isEmpty()) { // This should not happen, if so the user // is trying to add an unsatisfiable sentence // to the KB. throw new ArgumentException( "Attempted to add unsatisfiable sentence to KB, orig=[" + aSentence + "] CNF=" + cnfOfOrig); } // Ensure all clauses added to the KB are Standardized Apart. Clause c2 = _standardizeApart.standardizeApart(c, variableIndexical); // Will make all clauses immutable // so that they cannot be modified externally. c2.setImmutable(); if (!clauses.Contains(c2)) { clauses.Add(c2); // If added keep track of special types of // clauses, as useful for query purposes if (c2.isDefiniteClause()) { allDefiniteClauses.Add(c2); } if (c2.isImplicationDefiniteClause()) { implicationDefiniteClauses.Add(c2); } if (c2.isUnitClause()) { indexFact(c2.getLiterals().First()); } } } } // Only if it is a unit clause does it get indexed as a fact // see pg. 279 for general idea. private void indexFact(Literal fact) { String factKey = getFactKey(fact); if (!indexFacts.ContainsKey(factKey)) { indexFacts.Add(factKey, new List()); } indexFacts[factKey].Add(fact); } private void recursiveFetch(Dictionary theta, Literal l, List remainingLiterals, List> possibleSubstitutions) { // Find all substitutions for current predicate based on the // substitutions of prior predicates in the list (i.e. SUBST with // theta). List> pSubsts = fetch(subst(theta, l)); // No substitutions, therefore cannot continue if (null == pSubsts) { return; } foreach (Dictionary psubst in pSubsts) { // Ensure all prior substitution information is maintained // along the chain of predicates (i.e. for shared variables // across the predicates). foreach (Variable key in theta.Keys) { psubst.Add(key, theta[key]); } if (remainingLiterals.Count == 0) { // This means I am at the end of the chain of predicates // and have found a valid substitution. possibleSubstitutions.Add(psubst); } else { // Need to move to the next link in the chain of substitutions Literal first = remainingLiterals[0]; List rest = remainingLiterals.Skip(1).ToList(); recursiveFetch(psubst, first, rest, possibleSubstitutions); } } } private List fetchMatchingFacts(Literal l) { if (!indexFacts.ContainsKey(getFactKey(l))) { return null; } return indexFacts[getFactKey(l)]; } private String getFactKey(Literal l) { StringBuilder key = new StringBuilder(); if (l.isPositiveLiteral()) { key.Append("+"); } else { key.Append("-"); } key.Append(l.getAtomicSentence().getSymbolicName()); return key.ToString(); } } } ================================================ FILE: aima-csharp/logic/fol/kb/FOLKnowledgeBaseFactory.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.domain; using aima.core.logic.fol.inference; namespace aima.core.logic.fol.kb { /** * @author Ciaran O'Reilly * */ public class FOLKnowledgeBaseFactory { public static FOLKnowledgeBase createKingsKnowledgeBase( InferenceProcedure infp) { FOLKnowledgeBase kb = new FOLKnowledgeBase(DomainFactory.kingsDomain(), infp); kb.tell("((King(x) AND Greedy(x)) => Evil(x))"); kb.tell("King(John)"); kb.tell("King(Richard)"); kb.tell("Greedy(John)"); return kb; } public static FOLKnowledgeBase createWeaponsKnowledgeBase( InferenceProcedure infp) { FOLKnowledgeBase kb = new FOLKnowledgeBase(DomainFactory .weaponsDomain(), infp); kb .tell("( (((American(x) AND Weapon(y)) AND Sells(x,y,z)) AND Hostile(z)) => Criminal(x))"); kb.tell(" Owns(Nono, M1)"); kb.tell(" Missile(M1)"); kb.tell("((Missile(x) AND Owns(Nono,x)) => Sells(West,x,Nono))"); kb.tell("(Missile(x) => Weapon(x))"); kb.tell("(Enemy(x,America) => Hostile(x))"); kb.tell("American(West)"); kb.tell("Enemy(Nono,America)"); return kb; } public static FOLKnowledgeBase createLovesAnimalKnowledgeBase( InferenceProcedure infp) { FOLKnowledgeBase kb = new FOLKnowledgeBase(DomainFactory .lovesAnimalDomain(), infp); kb .tell("FORALL x (FORALL y (Animal(y) => Loves(x, y)) => EXISTS y Loves(y, x))"); kb .tell("FORALL x (EXISTS y (Animal(y) AND Kills(x, y)) => FORALL z NOT(Loves(z, x)))"); kb.tell("FORALL x (Animal(x) => Loves(Jack, x))"); kb.tell("(Kills(Jack, Tuna) OR Kills(Curiosity, Tuna))"); kb.tell("Cat(Tuna)"); kb.tell("FORALL x (Cat(x) => Animal(x))"); return kb; } public static FOLKnowledgeBase createRingOfThievesKnowledgeBase( InferenceProcedure infp) { FOLKnowledgeBase kb = new FOLKnowledgeBase(DomainFactory .ringOfThievesDomain(), infp); // s(x) => ~c(x) One who skis never gets caught kb.tell("(Skis(x) => NOT(Caught(x)))"); // c(x) => ~s(x) Those who are caught don't ever ski kb.tell("(Caught(x) => NOT(Skis(x)))"); // p(x,y) & c(y) => s(x) Jailbird parents have skiing kids kb.tell("((Parent(x,y) AND Caught(y)) => Skis(x))"); // s(x) & f(x,y) => s(y) All friends ski together kb.tell("(Skis(x) AND Friend(x,y) => Skis(y))"); // f(x,y) => f(y,x) Friendship is symmetric kb.tell("(Friend(x,y) => Friend(y,x))"); // FACTS // 1. { p(Mike,Joe) } Premise kb.tell("Parent(Mike, Joe)"); // 2. { p(Janet,Joe) } Premise kb.tell("Parent(Janet,Joe)"); // 3. { p(Nancy,Mike) } Premise kb.tell("Parent(Nancy,Mike)"); // 4. { p(Ernie,Janet) } Premise kb.tell("Parent(Ernie,Janet)"); // 5. { p(Bert,Nancy) } Premise kb.tell("Parent(Bert,Nancy)"); // 6. { p(Red,Ernie) } Premise kb.tell("Parent(Red,Ernie)"); // 7. { f(Red,Bert) } Premise kb.tell("Friend(Red,Bert)"); // 8. { f(Drew,Nancy) } Premise kb.tell("Friend(Drew,Nancy)"); // 9. { c(Mike) } Premise kb.tell("Caught(Mike)"); // 10. { c(Ernie) } Premise kb.tell("Caught(Ernie)"); return kb; } // Note: see - // http://logic.stanford.edu/classes/cs157/2008/lectures/lecture15.pdf // slide 12 for where this test example was taken from. public static FOLKnowledgeBase createABCEqualityKnowledgeBase( InferenceProcedure infp, bool includeEqualityAxioms) { FOLDomain domain = new FOLDomain(); domain.addConstant("A"); domain.addConstant("B"); domain.addConstant("C"); FOLKnowledgeBase kb = new FOLKnowledgeBase(domain, infp); kb.tell("B = A"); kb.tell("B = C"); if (includeEqualityAxioms) { // Reflexivity Axiom kb.tell("x = x"); // Symmetry Axiom kb.tell("(x = y => y = x)"); // Transitivity Axiom kb.tell("((x = y AND y = z) => x = z)"); } return kb; } // Note: see - // http://logic.stanford.edu/classes/cs157/2008/lectures/lecture15.pdf // slide 16,17, and 18 for where this test example was taken from. public static FOLKnowledgeBase createABCDEqualityAndSubstitutionKnowledgeBase( InferenceProcedure infp, bool includeEqualityAxioms) { FOLDomain domain = new FOLDomain(); domain.addConstant("A"); domain.addConstant("B"); domain.addConstant("C"); domain.addConstant("D"); domain.addPredicate("P"); domain.addFunction("F"); FOLKnowledgeBase kb = new FOLKnowledgeBase(domain, infp); kb.tell("F(A) = B"); kb.tell("F(B) = A"); kb.tell("C = D"); kb.tell("P(A)"); kb.tell("P(C)"); if (includeEqualityAxioms) { // Reflexivity Axiom kb.tell("x = x"); // Symmetry Axiom kb.tell("(x = y => y = x)"); // Transitivity Axiom kb.tell("((x = y AND y = z) => x = z)"); // Function F Substitution Axiom kb.tell("((x = y AND F(y) = z) => F(x) = z)"); // Predicate P Substitution Axiom kb.tell("((x = y AND P(y)) => P(x))"); } return kb; } } } ================================================ FILE: aima-csharp/logic/fol/kb/data/CNF.cs ================================================ using System; using System.Collections; using System.Text; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; namespace aima.core.logic.fol.kb.data { /** * Conjunctive Normal Form (CNF) : a conjunction of clauses, where each clause * is a disjunction of literals. * * @author Ciaran O'Reilly * */ public class CNF { private List conjunctionOfClauses = new List(); public CNF(List conjunctionOfClauses) { this.conjunctionOfClauses.AddRange(conjunctionOfClauses); } public int getNumberOfClauses() { return conjunctionOfClauses.Count; } public List getConjunctionOfClauses() { return new ReadOnlyCollection(conjunctionOfClauses).ToList(); } public override String ToString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < conjunctionOfClauses.Count; i++) { if (i > 0) { sb.Append(","); } sb.Append(conjunctionOfClauses[i].ToString()); } return sb.ToString(); } } } ================================================ FILE: aima-csharp/logic/fol/kb/data/Chain.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.inference.proof; namespace aima.core.logic.fol.kb.data { /** * * A Chain is a sequence of literals (while a clause is a set) - order is * important for a chain. * * @see Chain * * @author Ciaran O'Reilly * */ public class Chain { private static readonly List _emptyLiteralsList = new List(); private List literals = new List(); private ProofStep proofStep = null; public Chain() { // i.e. the empty chain } public Chain(List literals) { this.literals.AddRange(literals); } public ProofStep getProofStep() { if (null == proofStep) { // Assume was a premise proofStep = new ProofStepPremise(this); } return proofStep; } public void setProofStep(ProofStep proofStep) { this.proofStep = proofStep; } public bool isEmpty() { return literals.Count == 0; } public void addLiteral(Literal literal) { literals.Add(literal); } public Literal getHead() { if (0 == literals.Count) { return null; } return literals[0]; } public List getTail() { if (0 == literals.Count) { return _emptyLiteralsList; } return new ReadOnlyCollection(literals.Skip(1).ToList()).ToList(); } public int getNumberLiterals() { return literals.Count; } public List getLiterals() { return new ReadOnlyCollection(literals).ToList(); } /** * A contrapositive of a chain is a permutation in which a different literal * is placed at the front. The contrapositives of a chain are logically * equivalent to the original chain. * * @return a list of contrapositives for this chain. */ public List getContrapositives() { List contrapositives = new List(); List lits = new List(); for (int i = 1; i < literals.Count; i++) { lits.Clear(); lits.Add(literals[i]); lits.AddRange(literals.Take(i)); lits.AddRange(literals.GetRange(i + 1, literals.Count)); Chain cont = new Chain(lits); cont.setProofStep(new ProofStepChainContrapositive(cont, this)); contrapositives.Add(cont); } return contrapositives; } public override String ToString() { StringBuilder sb = new StringBuilder(); sb.Append("<"); for (int i = 0; i < literals.Count; i++) { if (i > 0) { sb.Append(","); } sb.Append(literals[i].ToString()); } sb.Append(">"); return sb.ToString(); } } } ================================================ FILE: aima-csharp/logic/fol/kb/data/Clause.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Collections.ObjectModel; using System.Linq; using aima.core.logic.fol; using aima.core.logic.fol.inference.proof; using aima.core.logic.fol.parsing; using aima.core.logic.fol.parsing.ast; using aima.core.util; namespace aima.core.logic.fol.kb.data { /** * A Clause: A disjunction of literals. * * * @author Ciaran O'Reilly * @author Tobias Barth * */ public class Clause { private static StandardizeApartIndexical _saIndexical = StandardizeApartIndexicalFactory .newStandardizeApartIndexical('c'); private static Unifier _unifier = new Unifier(); private static SubstVisitor _substVisitor = new SubstVisitor(); private static VariableCollector _variableCollector = new VariableCollector(); private static StandardizeApart _standardizeApart = new StandardizeApart(); private static LiteralsSorter _literalSorter = new LiteralsSorter(); private List literals = new List(); private List positiveLiterals = new List(); private List negativeLiterals = new List(); private bool immutable = false; private bool saCheckRequired = true; private String equalityIdentity = ""; private List factors = null; private List nonTrivialFactors = null; private String stringRep = null; private ProofStep proofStep = null; public Clause() { // i.e. the empty clause } public Clause(List lits) { this.literals.AddRange(lits); foreach (Literal l in literals) { if (l.isPositiveLiteral()) { this.positiveLiterals.Add(l); } else { this.negativeLiterals.Add(l); } } recalculateIdentity(); } public Clause(List lits1, List lits2) { literals.AddRange(lits1); literals.AddRange(lits2); foreach (Literal l in literals) { if (l.isPositiveLiteral()) { this.positiveLiterals.Add(l); } else { this.negativeLiterals.Add(l); } } recalculateIdentity(); } public ProofStep getProofStep() { if (null == proofStep) { // Assume was a premise proofStep = new ProofStepPremise(this); } return proofStep; } public void setProofStep(ProofStep proofStep) { this.proofStep = proofStep; } public bool isImmutable() { return immutable; } public void setImmutable() { immutable = true; } public bool isStandardizedApartCheckRequired() { return saCheckRequired; } public void setStandardizedApartCheckNotRequired() { saCheckRequired = false; } public bool isEmpty() { return literals.Count == 0; } public bool isUnitClause() { return literals.Count == 1; } public bool isDefiniteClause() { // A Definite Clause is a disjunction of literals of which exactly 1 is // positive. return !isEmpty() && positiveLiterals.Count == 1; } public bool isImplicationDefiniteClause() { // An Implication Definite Clause is a disjunction of literals of // which exactly 1 is positive and there is 1 or more negative // literals. return isDefiniteClause() && negativeLiterals.Count >= 1; } public bool isHornClause() { // A Horn clause is a disjunction of literals of which at most one is // positive. return !isEmpty() && positiveLiterals.Count <= 1; } public bool isTautology() { foreach (Literal pl in positiveLiterals) { // Literals in a clause must be exact complements // for tautology elimination to apply. Do not // remove non-identical literals just because // they are complements under unification, see pg16: // http://logic.stanford.edu/classes/cs157/2008/notes/chap09.pdf foreach (Literal nl in negativeLiterals) { if (pl.getAtomicSentence().Equals(nl.getAtomicSentence())) { return true; } } } return false; } public void addLiteral(Literal literal) { if (isImmutable()) { throw new InvalidOperationException( "Clause is immutable, cannot be updated."); } int origSize = literals.Count; literals.Add(literal); if (literals.Count > origSize) { if (literal.isPositiveLiteral()) { positiveLiterals.Add(literal); } else { negativeLiterals.Add(literal); } } recalculateIdentity(); } public void addPositiveLiteral(AtomicSentence atom) { addLiteral(new Literal(atom)); } public void addNegativeLiteral(AtomicSentence atom) { addLiteral(new Literal(atom, true)); } public int getNumberLiterals() { return literals.Count; } public int getNumberPositiveLiterals() { return positiveLiterals.Count; } public int getNumberNegativeLiterals() { return negativeLiterals.Count; } public List getLiterals() { return new ReadOnlyCollection(literals).ToList(); } public List getPositiveLiterals() { return new ReadOnlyCollection(positiveLiterals).ToList(); } public List getNegativeLiterals() { return new ReadOnlyCollection(negativeLiterals).ToList(); } public List getFactors() { if (null == factors) { calculateFactors(null); } return new ReadOnlyCollection(factors).ToList(); } public List getNonTrivialFactors() { if (null == nonTrivialFactors) { calculateFactors(null); } return new ReadOnlyCollection(nonTrivialFactors).ToList(); } public bool subsumes(Clause othC) { bool subsumes = false; // Equality is not subsumption if (!(this == othC)) { // Ensure this has less literals total and that // it is a subset of the other clauses positive and negative counts if (this.getNumberLiterals() < othC.getNumberLiterals() && this.getNumberPositiveLiterals() <= othC .getNumberPositiveLiterals() && this.getNumberNegativeLiterals() <= othC .getNumberNegativeLiterals()) { Dictionary> thisToTry = collectLikeLiterals(this.literals); Dictionary> othCToTry = collectLikeLiterals(othC.literals); // Ensure all like literals from this clause are a subset // of the other clause. bool contained = !thisToTry.Keys.Except(othCToTry.Keys).Any(); if (contained) { bool isAPossSubset = true; // Ensure that each set of same named literals // from this clause is a subset of the other // clauses same named literals. foreach (String pk in thisToTry.Keys) { if (thisToTry[pk].Count > othCToTry[pk].Count) { isAPossSubset = false; break; } } if (isAPossSubset) { // At this point I know this this Clause's // literal/arity names are a subset of the // other clauses literal/arity names subsumes = checkSubsumes(othC, thisToTry, othCToTry); } } } } return subsumes; } // Note: Applies binary resolution rule and factoring // Note: returns a set with an empty clause if both clauses // are empty, otherwise returns a set of binary resolvents. public List binaryResolvents(Clause othC) { List resolvents = new List(); // Resolving two empty clauses // gives you an empty clause if (isEmpty() && othC.isEmpty()) { resolvents.Add(new Clause()); return resolvents; } // Ensure Standardized Apart // Before attempting binary resolution othC = saIfRequired(othC); List allPosLits = new List(); List allNegLits = new List(); allPosLits.AddRange(this.positiveLiterals); allPosLits.AddRange(othC.positiveLiterals); allNegLits.AddRange(this.negativeLiterals); allNegLits.AddRange(othC.negativeLiterals); List trPosLits = new List(); List trNegLits = new List(); List copyRPosLits = new List(); List copyRNegLits = new List(); for (int i = 0; i < 2; i++) { trPosLits.Clear(); trNegLits.Clear(); if (i == 0) { // See if this clauses positives // unify with the other clauses // negatives trPosLits.AddRange(this.positiveLiterals); trNegLits.AddRange(othC.negativeLiterals); } else { // Try the other way round now trPosLits.AddRange(othC.positiveLiterals); trNegLits.AddRange(this.negativeLiterals); } // Now check to see if they resolve Dictionary copyRBindings = new Dictionary(); foreach (Literal pl in trPosLits) { foreach (Literal nl in trNegLits) { copyRBindings.Clear(); if (null != _unifier.unify(pl.getAtomicSentence(), nl .getAtomicSentence(), copyRBindings)) { copyRPosLits.Clear(); copyRNegLits.Clear(); bool found = false; foreach (Literal l in allPosLits) { if (!found && pl.Equals(l)) { found = true; continue; } copyRPosLits.Add(_substVisitor.subst(copyRBindings, l)); } found = false; foreach (Literal l in allNegLits) { if (!found && nl.Equals(l)) { found = true; continue; } copyRNegLits.Add(_substVisitor.subst(copyRBindings, l)); } // Ensure the resolvents are standardized apart Dictionary renameSubstitituon = _standardizeApart .standardizeApart(copyRPosLits, copyRNegLits, _saIndexical); Clause c = new Clause(copyRPosLits, copyRNegLits); c.setProofStep(new ProofStepClauseBinaryResolvent(c, this, othC, copyRBindings, renameSubstitituon)); if (isImmutable()) { c.setImmutable(); } if (!isStandardizedApartCheckRequired()) { c.setStandardizedApartCheckNotRequired(); } resolvents.Add(c); } } } } return resolvents; } public override String ToString() { if (null == stringRep) { List sortedLiterals = new List(literals); sortedLiterals.Sort(_literalSorter); stringRep = sortedLiterals.ToString(); } return stringRep; } public override int GetHashCode() { return equalityIdentity.GetHashCode(); } public override bool Equals(Object othObj) { if (null == othObj) { return false; } if (this == othObj) { return true; } if (!(othObj is Clause)) { return false; } Clause othClause = (Clause)othObj; return equalityIdentity.Equals(othClause.equalityIdentity); } public String getEqualityIdentity() { return equalityIdentity; } // // PRIVATE METHODS // private void recalculateIdentity() { lock (equalityIdentity) { // Sort the literals first based on negation, atomic sentence, // constant, function and variable. List sortedLiterals = new List(literals); sortedLiterals.Sort(_literalSorter); // All variables are considered the same as regards // sorting. Therefore, to determine if two clauses // are equivalent you need to determine // the # of unique variables they contain and // there positions across the clauses ClauseEqualityIdentityConstructor ceic = new ClauseEqualityIdentityConstructor( sortedLiterals, _literalSorter); equalityIdentity = ceic.getIdentity(); // Reset, these as will need to re-calcualte // if requested for again, best to only // access lazily. factors = null; nonTrivialFactors = null; // Reset the objects string representation // until it is requested for. stringRep = null; } } private void calculateFactors(List parentFactors) { nonTrivialFactors = new List(); Dictionary theta = new Dictionary(); List lits = new List(); for (int i = 0; i < 2; i++) { lits.Clear(); if (i == 0) { // Look at the positive literals lits.AddRange(positiveLiterals); } else { // Look at the negative literals lits.AddRange(negativeLiterals); } for (int x = 0; x < lits.Count; x++) { for (int y = x + 1; y < lits.Count; y++) { Literal litX = lits[x]; Literal litY = lits[y]; theta.Clear(); Dictionary substitution = _unifier.unify(litX .getAtomicSentence(), litY.getAtomicSentence(), theta); if (null != substitution) { List posLits = new List(); List negLits = new List(); if (i == 0) { posLits .Add(_substVisitor .subst(substitution, litX)); } else { negLits .Add(_substVisitor .subst(substitution, litX)); } foreach (Literal pl in positiveLiterals) { if (pl == litX || pl == litY) { continue; } posLits.Add(_substVisitor.subst(substitution, pl)); } foreach (Literal nl in negativeLiterals) { if (nl == litX || nl == litY) { continue; } negLits.Add(_substVisitor.subst(substitution, nl)); } // Ensure the non trivial factor is standardized apart _standardizeApart.standardizeApart(posLits, negLits, _saIndexical); Clause c = new Clause(posLits, negLits); c.setProofStep(new ProofStepClauseFactor(c, this, null, null, null, null)); if (isImmutable()) { c.setImmutable(); } if (!isStandardizedApartCheckRequired()) { c.setStandardizedApartCheckNotRequired(); } if (null == parentFactors) { c.calculateFactors(nonTrivialFactors); nonTrivialFactors.AddRange(c.getFactors()); } else { if (!parentFactors.Contains(c)) { c.calculateFactors(nonTrivialFactors); nonTrivialFactors.AddRange(c.getFactors()); } } } } } } factors = new List(); // Need to add self, even though a non-trivial // factor. See: slide 30 // http://logic.stanford.edu/classes/cs157/2008/lectures/lecture10.pdf // for example of incompleteness when // trivial factor not included. factors.Add(this); factors.AddRange(nonTrivialFactors); } private Clause saIfRequired(Clause othClause) { // If performing resolution with self // then need to standardize apart in // order to work correctly. if (isStandardizedApartCheckRequired() || this == othClause) { List mVariables = _variableCollector .collectAllVariables(this); List oVariables = _variableCollector .collectAllVariables(othClause); List cVariables = new List(); cVariables.AddRange(mVariables); cVariables.AddRange(oVariables); if (cVariables.Count < (mVariables.Count + oVariables.Count)) { othClause = _standardizeApart.standardizeApart(othClause, _saIndexical); } } return othClause; } private Dictionary> collectLikeLiterals(List literals) { Dictionary> likeLiterals = new Dictionary>(); foreach (Literal l in literals) { // Want to ensure P(a, b) is considered different than P(a, b, c) // i.e. consider an atom's arity P/#. String literalName = (l.isNegativeLiteral() ? "~" : "") + l.getAtomicSentence().getSymbolicName() + "/" + l.getAtomicSentence().getArgs().Count; List like = null; if (likeLiterals.ContainsKey(literalName)) { like = likeLiterals[literalName]; } if (null == like) { like = new List(); likeLiterals.Add(literalName, like); } like.Add(l); } return likeLiterals; } private bool checkSubsumes(Clause othC, Dictionary> thisToTry, Dictionary> othCToTry) { bool subsumes = false; List thisTerms = new List(); List othCTerms = new List(); // Want to track possible number of permuations List radixs = new List(); foreach (String literalName in thisToTry.Keys) { int sizeT = thisToTry[literalName].Count; int sizeO = othCToTry[literalName].Count; if (sizeO > 1) { // The following is being used to // track the number of permutations // that can be mapped from the // other clauses like literals to this // clauses like literals. // i.e. n!/(n-r)! // where n=sizeO and r =sizeT for (int i = 0; i < sizeT; i++) { int r = sizeO - i; if (r > 1) { radixs.Add(r); } } } // Track the terms for this clause foreach (Literal tl in thisToTry[literalName]) { List folNodes = tl.getAtomicSentence().getArgs(); foreach (FOLNode n in folNodes) { thisTerms.Add((Term)n); } } } MixedRadixNumber permutation = null; long numPermutations = 1L; if (radixs.Count > 0) { permutation = new MixedRadixNumber(0, radixs); numPermutations = permutation.getMaxAllowedValue() + 1; } // Want to ensure none of the othCVariables are // part of the key set of a unification as // this indicates it is not a legal subsumption. List othCVariables = _variableCollector .collectAllVariables(othC); Dictionary theta = new Dictionary(); List literalPermuations = new List(); for (long l = 0L; l < numPermutations; l++) { // Track the other clause's terms for this // permutation. othCTerms.Clear(); int radixIdx = 0; foreach (String literalName in thisToTry.Keys) { int sizeT = thisToTry[literalName].Count; literalPermuations.Clear(); literalPermuations.AddRange(othCToTry[literalName]); int sizeO = literalPermuations.Count; if (sizeO > 1) { for (int i = 0; i < sizeT; i++) { int r = sizeO - i; if (r > 1) { // If not a 1 to 1 mapping then you need // to use the correct permuation int numPos = permutation .getCurrentNumeralValue(radixIdx); Literal lit = literalPermuations[numPos]; literalPermuations.Remove(lit); foreach (FOLNode arg in lit.getAtomicSentence().getArgs()) { othCTerms.Add((Term)arg); } radixIdx++; } else { // is the last mapping, therefore // won't be on the radix foreach (FOLNode arg in literalPermuations[0].getAtomicSentence().getArgs()) { othCTerms.Add((Term)arg); } } } } else { // a 1 to 1 mapping foreach (FOLNode arg in literalPermuations[0].getAtomicSentence().getArgs()) { othCTerms.Add((Term)arg); } } } // Note: on unifier // unifier.unify(P(w, x), P(y, z)))={w=y, x=z} // unifier.unify(P(y, z), P(w, x)))={y=w, z=x} // Therefore want this clause to be the first // so can do the othCVariables check for an invalid // subsumes. theta.Clear(); List termNodes = new List(); foreach (Term t in thisTerms) { termNodes.Add((FOLNode)t); } List othCNodes = new List(); foreach (Term t in othCTerms) { othCNodes.Add((FOLNode)t); } if (null != _unifier.unify(termNodes, othCNodes, theta)) { bool containsAny = false; foreach (Variable v in theta.Keys) { if (othCVariables.Contains(v)) { containsAny = true; break; } } if (!containsAny) { subsumes = true; break; } } // If there is more than 1 mapping // keep track of where I am in the // possible number of mapping permutations. if (null != permutation) { permutation.increment(); } } return subsumes; } } class LiteralsSorter : IComparer { public int Compare(Literal o1, Literal o2) { int rVal = 0; // If literals are not negated the same // then positive literals are considered // (by convention here) to be of higher // order than negative literals if (o1.isPositiveLiteral() != o2.isPositiveLiteral()) { if (o1.isPositiveLiteral()) { return 1; } return -1; } // Check their symbolic names for order first rVal = o1.getAtomicSentence().getSymbolicName().CompareTo( o2.getAtomicSentence().getSymbolicName()); // If have same symbolic names // then need to compare individual arguments // for order. if (0 == rVal) { rVal = compareArgs(o1.getAtomicSentence().getArgs(), o2 .getAtomicSentence().getArgs()); } return rVal; } private int compareArgs(List args1, List args2) { int rVal = 0; // Compare argument sizes first rVal = args1.Count - args2.Count; if (0 == rVal && args1.Count > 0) { // Move forward and compare the // first arguments Term t1 = (Term)args1[0]; Term t2 = (Term)args2[0]; if (t1.GetType().Equals(t2.GetType())) { // Note: Variables are considered to have // the same order if (t1 is Constant) { rVal = t1.getSymbolicName().CompareTo(t2.getSymbolicName()); } else if (t1 is Function) { rVal = t1.getSymbolicName().CompareTo(t2.getSymbolicName()); if (0 == rVal) { // Same function names, therefore // compare the function arguments rVal = compareArgs(t1.getArgs(), t2.getArgs()); } } // If the first args are the same // then compare the ordering of the // remaining arguments if (0 == rVal) { rVal = compareArgs(args1.Skip(1).ToList(), args2 .Skip(1).ToList()); } } else { // Order for different Terms is: // Constant > Function > Variable if (t1 is Constant) { rVal = 1; } else if (t2 is Constant) { rVal = -1; } else if (t1 is Function) { rVal = 1; } else { rVal = -1; } } } return rVal; } } class ClauseEqualityIdentityConstructor : FOLVisitor { private StringBuilder identity = new StringBuilder(); private int noVarPositions = 0; private int[] clauseVarCounts = null; private int currentLiteral = 0; private Dictionary> varPositions = new Dictionary>(); public ClauseEqualityIdentityConstructor(List literals, LiteralsSorter sorter) { clauseVarCounts = new int[literals.Count]; foreach (Literal l in literals) { if (l.isNegativeLiteral()) { identity.Append("~"); } identity.Append(l.getAtomicSentence().getSymbolicName()); identity.Append("("); bool firstTerm = true; foreach (Term t in l.getAtomicSentence().getArgs()) { if (firstTerm) { firstTerm = false; } else { identity.Append(","); } t.accept(this, null); } identity.Append(")"); currentLiteral++; } int min, max; min = max = 0; for (int i = 0; i < literals.Count; i++) { int incITo = i; int next = i + 1; max += clauseVarCounts[i]; while (next < literals.Count) { if (0 != sorter.Compare(literals[i], literals[next])) { break; } max += clauseVarCounts[next]; incITo = next; // Need to skip to the end of the range next++; } // This indicates two or more literals are identical // except for variable naming (note: identical // same name would be removed as are working // with sets so don't need to worry about this). if ((next - i) > 1) { // Need to check each variable // and if it has a position within the // current min/max range then need // to include its alternative // sort order positions as well foreach (String key in varPositions.Keys) { List positions = varPositions[key]; List additPositions = new List(); // Add then subtract for all possible // positions in range foreach (int pos in positions) { if (pos >= min && pos < max) { int pPos = pos; int nPos = pos; for (int candSlot = i; candSlot < (next - 1); candSlot++) { pPos += clauseVarCounts[i]; if (pPos >= min && pPos < max) { if (!positions.Contains(pPos) && !additPositions.Contains(pPos)) { additPositions.Add(pPos); } } nPos -= clauseVarCounts[i]; if (nPos >= min && nPos < max) { if (!positions.Contains(nPos) && !additPositions.Contains(nPos)) { additPositions.Add(nPos); } } } } } positions.AddRange(additPositions); } } min = max; i = incITo; } // Determine the maxWidth int maxWidth = 1; while (noVarPositions >= 10) { noVarPositions = noVarPositions / 10; maxWidth++; } String format = "%0" + maxWidth + "d"; // Sort the individual position lists // And then add their string representations // together List varOffsets = new List(); foreach (String key in varPositions.Keys) { List positions = varPositions[key]; positions.Sort(); StringBuilder sb = new StringBuilder(); foreach (int pos in positions) { sb.Append(String.Format(format, pos)); } varOffsets.Add(sb.ToString()); } varOffsets.Sort(); for (int i = 0; i < varOffsets.Count; i++) { identity.Append(varOffsets[i]); if (i < (varOffsets.Count - 1)) { identity.Append(","); } } } public String getIdentity() { return identity.ToString(); } // START-FOLVisitor public Object visitVariable(Variable var, Object arg) { // All variables will be marked with an * identity.Append("*"); List positions = null; if (varPositions.ContainsKey(var.getValue())) { positions = varPositions[var.getValue()]; } if (null == positions) { positions = new List(); varPositions.Add(var.getValue(), positions); } positions.Add(noVarPositions); noVarPositions++; clauseVarCounts[currentLiteral]++; return var; } public Object visitConstant(Constant constant, Object arg) { identity.Append(constant.getValue()); return constant; } public Object visitFunction(Function function, Object arg) { bool firstTerm = true; identity.Append(function.getFunctionName()); identity.Append("("); foreach (Term t in function.getTerms()) { if (firstTerm) { firstTerm = false; } else { identity.Append(","); } t.accept(this, arg); } identity.Append(")"); return function; } public Object visitPredicate(Predicate predicate, Object arg) { throw new InvalidOperationException("Should not be called"); } public Object visitTermEquality(TermEquality equality, Object arg) { throw new InvalidOperationException("Should not be called"); } public Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { throw new InvalidOperationException("Should not be called"); } public Object visitNotSentence(NotSentence sentence, Object arg) { throw new InvalidOperationException("Should not be called"); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { throw new InvalidOperationException("Should not be called"); } // END-FOLVisitor } } ================================================ FILE: aima-csharp/logic/fol/kb/data/Literal.cs ================================================ using System; using System.Collections; using System.Text; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.kb.data { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 244.
*
* A literal is either an atomic sentence (a positive literal) or a negated * atomic sentence (a negative literal). * * @author Ciaran O'Reilly * */ public class Literal { private AtomicSentence atom = null; private bool negativeLiteral = false; private String strRep = null; private int hashCode = 0; public Literal(AtomicSentence atom) { this.atom = atom; } public Literal(AtomicSentence atom, bool negated) { this.atom = atom; this.negativeLiteral = negated; } public virtual Literal newInstance(AtomicSentence atom) { return new Literal(atom, negativeLiteral); } public bool isPositiveLiteral() { return !negativeLiteral; } public bool isNegativeLiteral() { return negativeLiteral; } public AtomicSentence getAtomicSentence() { return atom; } public override String ToString() { if (null == strRep) { StringBuilder sb = new StringBuilder(); if (isNegativeLiteral()) { sb.Append("~"); } sb.Append(getAtomicSentence().ToString()); strRep = sb.ToString(); } return strRep; } public override bool Equals(Object o) { if (this == o) { return true; } if (o.GetType() != this.GetType()) { // This prevents ReducedLiterals // being treated as equivalent to // normal Literals. return false; } if (!(o is Literal)) { return false; } Literal l = (Literal)o; return l.isPositiveLiteral() == isPositiveLiteral() && l.getAtomicSentence().getSymbolicName().Equals( atom.getSymbolicName()) && l.getAtomicSentence().getArgs().Equals(atom.getArgs()); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode = 37 * hashCode + this.GetType().Name.GetHashCode() + (isPositiveLiteral() ? "+".GetHashCode() : "-".GetHashCode()) + atom.getSymbolicName().GetHashCode(); foreach (Term t in atom.getArgs()) { hashCode = 37 * hashCode + t.GetHashCode(); } } return hashCode; } } } ================================================ FILE: aima-csharp/logic/fol/kb/data/ReducedLiteral.cs ================================================ using System; using System.Collections.Generic; using System.Text; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.kb.data { /** * @see Reduced Literal * * @author Ciaran O'Reilly * */ public class ReducedLiteral : Literal { private String strRep = null; public ReducedLiteral(AtomicSentence atom) : base(atom) { } public ReducedLiteral(AtomicSentence atom, bool negated) : base(atom, negated) { } public override Literal newInstance(AtomicSentence atom) { return new ReducedLiteral(atom, isNegativeLiteral()); } public override String ToString() { if (null == strRep) { StringBuilder sb = new StringBuilder(); sb.Append("["); if (isNegativeLiteral()) { sb.Append("~"); } sb.Append(getAtomicSentence().ToString()); sb.Append("]"); strRep = sb.ToString(); } return strRep; } } } ================================================ FILE: aima-csharp/logic/fol/parsing/AbstractFOLVisitor.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.parsing { /** * @author Ravi Mohan * */ public class AbstractFOLVisitor : FOLVisitor { public AbstractFOLVisitor() { } protected Sentence recreate(Object ast) { return ((Sentence)ast).copySentence(); } public virtual Object visitVariable(Variable variable, Object arg) { return variable.copy(); } public virtual Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg) { List variables = new List(); foreach (Variable var in sentence.getVariables()) { variables.Add((Variable)var.accept(this, arg)); } return new QuantifiedSentence(sentence.getQuantifier(), variables, (Sentence)sentence.getQuantified().accept(this, arg)); } public Object visitPredicate(Predicate predicate, Object arg) { List terms = predicate.getTerms(); List newTerms = new List(); for (int i = 0; i < terms.Count; i++) { Term t = terms[i]; Term subsTerm = (Term)t.accept(this, arg); newTerms.Add(subsTerm); } return new Predicate(predicate.getPredicateName(), newTerms); } public Object visitTermEquality(TermEquality equality, Object arg) { Term newTerm1 = (Term)equality.getTerm1().accept(this, arg); Term newTerm2 = (Term)equality.getTerm2().accept(this, arg); return new TermEquality(newTerm1, newTerm2); } public Object visitConstant(Constant constant, Object arg) { return constant; } public Object visitFunction(Function function, Object arg) { List terms = function.getTerms(); List newTerms = new List(); for (int i = 0; i < terms.Count; i++) { Term t = terms[i]; Term subsTerm = (Term)t.accept(this, arg); newTerms.Add(subsTerm); } return new Function(function.getFunctionName(), newTerms); } public Object visitNotSentence(NotSentence sentence, Object arg) { return new NotSentence((Sentence)sentence.getNegated().accept(this, arg)); } public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { Sentence substFirst = (Sentence)sentence.getFirst().accept(this, arg); Sentence substSecond = (Sentence)sentence.getSecond() .accept(this, arg); return new ConnectedSentence(sentence.getConnector(), substFirst, substSecond); } } } ================================================ FILE: aima-csharp/logic/fol/parsing/FOLLexer.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using aima.core.logic.common; using aima.core.logic.fol; using aima.core.logic.fol.domain; namespace aima.core.logic.fol.parsing { /** * @author Ciaran O'Reilly * @author Ravi Mohan * */ public class FOLLexer : Lexer { private FOLDomain domain; private HashSet connectors, quantifiers; public FOLLexer(FOLDomain domain) { this.domain = domain; connectors = new HashSet(); connectors.Add(Connectors.NOT); connectors.Add(Connectors.AND); connectors.Add(Connectors.OR); connectors.Add(Connectors.IMPLIES); connectors.Add(Connectors.BICOND); quantifiers = new HashSet(); quantifiers.Add(Quantifiers.FORALL); quantifiers.Add(Quantifiers.EXISTS); } public FOLDomain getFOLDomain() { return domain; } public override Token nextToken() { int startPosition = getCurrentPositionInInput(); if (lookAhead(1) == '(') { consume(); return new Token((int)LogicTokenTypes.LPAREN, "(", startPosition); } else if (lookAhead(1) == ')') { consume(); return new Token((int)LogicTokenTypes.RPAREN, ")", startPosition); } else if (lookAhead(1) == ',') { consume(); return new Token((int)LogicTokenTypes.COMMA, ",", startPosition); } else if (identifierDetected()) { // System.Console.WriteLine("identifier detected"); return identifier(); } else if (char.IsWhiteSpace(lookAhead(1))) { consume(); return nextToken(); } else if (lookAhead(1) == 65535) { return new Token((int)LogicTokenTypes.EOI, "EOI", startPosition); } else { throw new LexerException("Lexing error on character " + lookAhead(1) + " at position " + getCurrentPositionInInput(), getCurrentPositionInInput()); } } private bool isCSharpIdentifierStart(char c) { return char.IsLetter(c) || c == '_' || c == '$' || char.IsNumber(c); } private Token identifier() { int startPosition = getCurrentPositionInInput(); StringBuilder sbuf = new StringBuilder(); while ((isCSharpIdentifierStart(lookAhead(1))) || partOfConnector()) { sbuf.Append(lookAhead(1)); consume(); } String readString = sbuf.ToString(); // System.Console.WriteLine(readString); if (connectors.Contains(readString)) { return new Token((int)LogicTokenTypes.CONNECTOR, readString, startPosition); } else if (quantifiers.Contains(readString)) { return new Token((int)LogicTokenTypes.QUANTIFIER, readString, startPosition); } else if (domain.getPredicates().Contains(readString)) { return new Token((int)LogicTokenTypes.PREDICATE, readString, startPosition); } else if (domain.getFunctions().Contains(readString)) { return new Token((int)LogicTokenTypes.FUNCTION, readString, startPosition); } else if (domain.getConstants().Contains(readString)) { return new Token((int)LogicTokenTypes.CONSTANT, readString, startPosition); } else if (isVariable(readString)) { return new Token((int)LogicTokenTypes.VARIABLE, readString, startPosition); } else if (readString.Equals("=")) { return new Token((int)LogicTokenTypes.EQUALS, readString, startPosition); } else { throw new LexerException("Lexing error on character " + lookAhead(1) + " at position " + getCurrentPositionInInput(), getCurrentPositionInInput()); } } private bool isVariable(String s) { return (char.IsLower(s[0])); } private bool identifierDetected() { return (isCSharpIdentifierStart(((char)lookAheadBuffer[0])) || partOfConnector()); } private bool partOfConnector() { return (lookAhead(1) == '=') || (lookAhead(1) == '<') || (lookAhead(1) == '>'); } } } ================================================ FILE: aima-csharp/logic/fol/parsing/FOLParser.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.common; using aima.core.logic.fol.domain; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.parsing { /** * @author Ravi Mohan * */ public class FOLParser { private FOLLexer lexer; protected Token[] lookAheadBuffer; protected int LookAhead = 1; public FOLParser(FOLLexer lexer) { this.lexer = lexer; lookAheadBuffer = new Token[LookAhead]; } public FOLParser(FOLDomain domain): this(new FOLLexer(domain)) { } public FOLDomain getFOLDomain() { return lexer.getFOLDomain(); } public Sentence parse(String s) { setUpToParse(s); return parseSentence(); } public void setUpToParse(String s) { lookAheadBuffer = new Token[1]; lexer.setInput(s); fillLookAheadBuffer(); } private Term parseTerm() { Token t = lookAhead(1); int tokenType = t.getType(); if (tokenType == (int)LogicTokenTypes.CONSTANT) { return parseConstant(); } else if (tokenType == (int)LogicTokenTypes.VARIABLE) { return parseVariable(); } else if (tokenType == (int)LogicTokenTypes.FUNCTION) { return parseFunction(); } else { return null; } } public Term parseVariable() { Token t = lookAhead(1); String value = t.getText(); consume(); return new Variable(value); } public Term parseConstant() { Token t = lookAhead(1); String value = t.getText(); consume(); return new Constant(value); } public Term parseFunction() { Token t = lookAhead(1); String functionName = t.getText(); List terms = processTerms(); return new Function(functionName, terms); } public Sentence parsePredicate() { Token t = lookAhead(1); String predicateName = t.getText(); List terms = processTerms(); return new Predicate(predicateName, terms); } private List processTerms() { consume(); List terms = new List(); match("("); Term term = parseTerm(); terms.Add(term); while (lookAhead(1).getType() == (int)LogicTokenTypes.COMMA) { match(","); term = parseTerm(); terms.Add(term); } match(")"); return terms; } public Sentence parseTermEquality() { Term term1 = parseTerm(); match("="); // System.Console.WriteLine("="); Term term2 = parseTerm(); return new TermEquality(term1, term2); } public Sentence parseNotSentence() { match("NOT"); return new NotSentence(parseSentence()); } // PROTECTED METHODS protected Token lookAhead(int i) { return lookAheadBuffer[i - 1]; } protected void consume() { // System.Console.WriteLine("consuming" +lookAheadBuffer[0].getText()); loadNextTokenFromInput(); // System.Console.WriteLine("next token " +lookAheadBuffer[0].getText()); } protected void loadNextTokenFromInput() { bool eoiEncountered = false; for (int i = 0; i < LookAhead - 1; i++) { lookAheadBuffer[i] = lookAheadBuffer[i + 1]; if (isEndOfInput(lookAheadBuffer[i])) { eoiEncountered = true; break; } } if (!eoiEncountered) { try { lookAheadBuffer[LookAhead - 1] = lexer.nextToken(); } catch (Exception e) { Console.WriteLine(e); } } } protected bool isEndOfInput(Token t) { return (t.getType() == (int)LogicTokenTypes.EOI); } protected void fillLookAheadBuffer() { for (int i = 0; i < LookAhead; i++) { lookAheadBuffer[i] = lexer.nextToken(); } } protected void match(String terminalSymbol) { if (lookAhead(1).getText().Equals(terminalSymbol)) { consume(); } else { throw new ApplicationException( "Syntax error detected at match. Expected " + terminalSymbol + " but got " + lookAhead(1).getText()); } } // PRIVATE METHODS private Sentence parseSentence() { Token t = lookAhead(1); if (lParen(t)) { return parseParanthizedSentence(); } else if ((lookAhead(1).getType() == (int)LogicTokenTypes.QUANTIFIER)) { return parseQuantifiedSentence(); } else if (notToken(t)) { return parseNotSentence(); } else if (predicate(t)) { return parsePredicate(); } else if (term(t)) { return parseTermEquality(); } throw new ApplicationException("parse failed with Token " + t.getText()); } private Sentence parseQuantifiedSentence() { String quantifier = lookAhead(1).getText(); consume(); List variables = new List(); Variable var = (Variable)parseVariable(); variables.Add(var); while (lookAhead(1).getType() == (int)LogicTokenTypes.COMMA) { consume(); var = (Variable)parseVariable(); variables.Add(var); } Sentence sentence = parseSentence(); return new QuantifiedSentence(quantifier, variables, sentence); } private Sentence parseParanthizedSentence() { match("("); Sentence sen = parseSentence(); while (binaryConnector(lookAhead(1))) { String connector = lookAhead(1).getText(); consume(); Sentence other = parseSentence(); sen = new ConnectedSentence(connector, sen, other); } match(")"); return sen; /* new ParanthizedSentence */ } private bool binaryConnector(Token t) { if ((t.getType() == (int)LogicTokenTypes.CONNECTOR) && (!(t.getText().Equals("NOT")))) { return true; } else { return false; } } private bool lParen(Token t) { if (t.getType() == (int)LogicTokenTypes.LPAREN) { return true; } else { return false; } } private bool term(Token t) { if ((t.getType() == (int)LogicTokenTypes.FUNCTION) || (t.getType() == (int)LogicTokenTypes.CONSTANT) || (t.getType() == (int)LogicTokenTypes.VARIABLE)) { return true; } else { return false; } } private bool predicate(Token t) { if ((t.getType() == (int)LogicTokenTypes.PREDICATE)) { return true; } else { return false; } } private bool notToken(Token t) { if ((t.getType() == (int)LogicTokenTypes.CONNECTOR) && (t.getText().Equals("NOT"))) { return true; } else { return false; } } } } ================================================ FILE: aima-csharp/logic/fol/parsing/FOLVisitor.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing.ast; namespace aima.core.logic.fol.parsing { /** * @author Ravi Mohan * */ public interface FOLVisitor { Object visitPredicate(Predicate p, Object arg); Object visitTermEquality(TermEquality equality, Object arg); Object visitVariable(Variable variable, Object arg); Object visitConstant(Constant constant, Object arg); Object visitFunction(Function function, Object arg); Object visitNotSentence(NotSentence sentence, Object arg); Object visitConnectedSentence(ConnectedSentence sentence, Object arg); Object visitQuantifiedSentence(QuantifiedSentence sentence, Object arg); } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/AtomicSentence.cs ================================================ using System.Collections.Generic; namespace aima.core.logic.fol.parsing.ast { /** * @author Ciaran O'Reilly * */ public interface AtomicSentence : Sentence { List getArgs(); AtomicSentence copy(); } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/ConnectedSentence.cs ================================================ using System; using System.Collections.Generic; using System.Text; using aima.core.logic.fol.parsing; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class ConnectedSentence : Sentence { private String connector; private Sentence first, second; private List args = new List(); private String stringRep = null; private int hashCode = 0; public ConnectedSentence(String connector, Sentence first, Sentence second) { this.connector = connector; this.first = first; this.second = second; args.Add(first); args.Add(second); } public String getConnector() { return connector; } public Sentence getFirst() { return first; } public Sentence getSecond() { return second; } // START-Sentence public String getSymbolicName() { return getConnector(); } public bool isCompound() { return true; } public List getArgs() { Sentence[] copy = new Sentence[args.Count]; args.CopyTo(copy); return new List(copy); } public Object accept(FOLVisitor v, Object arg) { return v.visitConnectedSentence(this, arg); } public FOLNode copy() { return null; } public Sentence copySentence() { return new ConnectedSentence(connector, first.copySentence(), second.copySentence()); } // END-Sentence public override bool Equals(Object o) { if (this == o) { return true; } if ((o == null) || !(o is ConnectedSentence)) { return false; } ConnectedSentence cs = (ConnectedSentence)o; return cs.getConnector().Equals(getConnector()) && cs.getFirst().Equals(getFirst()) && cs.getSecond().Equals(getSecond()); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode = 37 * hashCode + getConnector().GetHashCode(); hashCode = 37 * hashCode + getFirst().GetHashCode(); hashCode = 37 * hashCode + getSecond().GetHashCode(); } return hashCode; } public override String ToString() { if (null == stringRep) { StringBuilder sb = new StringBuilder(); sb.Append("("); sb.Append(first.ToString()); sb.Append(" "); sb.Append(connector); sb.Append(" "); sb.Append(second.ToString()); sb.Append(")"); stringRep = sb.ToString(); } return stringRep; } } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/Constant.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class Constant : Term { private String value; private int hashCode = 0; public Constant(String s) { value = s; } public String getValue() { return value; } // START-Term public String getSymbolicName() { return getValue(); } public bool isCompound() { return false; } List FOLNode.getArgs() { return null; } public List getArgs() { // Is not Compound, therefore should // return null for its arguments return null; } public Object accept(FOLVisitor v, Object arg) { return v.visitConstant(this, arg); } FOLNode FOLNode.copy() { return copy(); } public Term copy() { return new Constant(value); } // END-Term public override bool Equals(Object o) { if (this == o) { return true; } if (!(o is Constant)) { return false; } Constant c = (Constant)o; return c.getValue().Equals(getValue()); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode = 37 * hashCode + value.GetHashCode(); } return hashCode; } public override String ToString() { return value; } } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/FOLNode.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.common; using aima.core.logic.fol.parsing; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface FOLNode : ParseTreeNode { String getSymbolicName(); bool isCompound(); List getArgs(); Object accept(FOLVisitor v, Object arg); FOLNode copy(); } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/Function.cs ================================================ using System; using System.Collections.Generic; using System.Text; using aima.core.logic.fol.parsing; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class Function : Term { private String functionName; private List terms = new List(); private String stringRep = null; private int hashCode = 0; public Function(String functionName, List terms) { this.functionName = functionName; this.terms.AddRange(terms); } public String getFunctionName() { return functionName; } public List getTerms() { Term[] copy = new Term[terms.Count]; terms.CopyTo(copy); return new List(copy); } // START-Term public String getSymbolicName() { return getFunctionName(); } public bool isCompound() { return true; } List FOLNode.getArgs() { return null; } public List getArgs() { return getTerms(); } public Object accept(FOLVisitor v, Object arg) { return v.visitFunction(this, arg); } FOLNode FOLNode.copy() { return copy(); } public Term copy() { List copyTerms = new List(); foreach (Term t in terms) { copyTerms.Add(t.copy()); } return new Function(functionName, copyTerms); } // END-Term public override bool Equals(Object o) { if (this == o) { return true; } if (!(o is Function)) { return false; } Function f = (Function)o; return f.getFunctionName().Equals(getFunctionName()) && f.getTerms().Equals(getTerms()); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode = 37 * hashCode + functionName.GetHashCode(); foreach (Term t in terms) { hashCode = 37 * hashCode + t.GetHashCode(); } } return hashCode; } public override String ToString() { if (null == stringRep) { StringBuilder sb = new StringBuilder(); sb.Append(functionName); sb.Append("("); bool first = true; foreach (Term t in terms) { if (first) { first = false; } else { sb.Append(","); } sb.Append(t.ToString()); } sb.Append(")"); stringRep = sb.ToString(); } return stringRep; } } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/NotSentence.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using aima.core.logic.fol; using aima.core.logic.fol.parsing; using aima.core.logic.fol.inference.proof; using System.Collections.ObjectModel; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class NotSentence : Sentence { private Sentence negated; private List args = new List(); private String stringRep = null; private int hashCode = 0; public NotSentence(Sentence negated) { this.negated = negated; args.Add(negated); } public Sentence getNegated() { return negated; } // START-Sentence public String getSymbolicName() { return Connectors.NOT; } public bool isCompound() { return true; } public List getArgs() { return new ReadOnlyCollection(args).ToList(); } public Object accept(FOLVisitor v, Object arg) { return v.visitNotSentence(this, arg); } public FOLNode copy() { return new NotSentence((Sentence)negated.copy()); } public Sentence copySentence() { return null; } // END-Sentence public override bool Equals(Object o) { if (this == o) { return true; } if ((o == null) || !(o is NotSentence)) { return false; } NotSentence ns = (NotSentence)o; return (ns.negated.Equals(negated)); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode = 37 * hashCode + negated.GetHashCode(); } return hashCode; } public override String ToString() { if (null == stringRep) { StringBuilder sb = new StringBuilder(); sb.Append("NOT("); sb.Append(negated.ToString()); sb.Append(")"); stringRep = sb.ToString(); } return stringRep; } } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/Predicate.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using aima.core.logic.fol.parsing; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class Predicate : AtomicSentence { private String predicateName; private List terms = new List(); private String stringRep = null; private int hashCode = 0; public Predicate(String predicateName, List terms) { this.predicateName = predicateName; this.terms.AddRange(terms); } public String getPredicateName() { return predicateName; } public List getTerms() { return terms.AsReadOnly().ToList(); } // START-AtomicSentence public String getSymbolicName() { return getPredicateName(); } public bool isCompound() { return true; } List FOLNode.getArgs() { return null; } AtomicSentence AtomicSentence.copy() { return null; } public List getArgs() { return getTerms(); } public Object accept(FOLVisitor v, Object arg) { return v.visitPredicate(this, arg); } public FOLNode copy() { List copyTerms = new List(); foreach (Term t in terms) { copyTerms.Add(t.copy()); } return new Predicate(predicateName, copyTerms); } public Sentence copySentence() { return null; } // END-AtomicSentence public override bool Equals(Object o) { if (this == o) { return true; } if (!(o is Predicate)) { return false; } Predicate p = (Predicate)o; return p.getPredicateName().Equals(getPredicateName()) && p.getTerms().Equals(getTerms()); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode = 37 * hashCode + predicateName.GetHashCode(); foreach (Term t in terms) { hashCode = 37 * hashCode + t.GetHashCode(); } } return hashCode; } public override String ToString() { if (null == stringRep) { StringBuilder sb = new StringBuilder(); sb.Append(predicateName); sb.Append("("); bool first = true; foreach (Term t in terms) { if (first) { first = false; } else { sb.Append(","); } sb.Append(t.ToString()); } sb.Append(")"); stringRep = sb.ToString(); } return stringRep; } } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/QuantifiedSentence.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.parsing; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class QuantifiedSentence : Sentence { private String quantifier; private List variables = new List(); private Sentence quantified; private List args = new List(); private String stringRep = null; private int hashCode = 0; public QuantifiedSentence(String quantifier, List variables, Sentence quantified) { this.quantifier = quantifier; this.variables.AddRange(variables); this.quantified = quantified; this.args.AddRange(variables); this.args.Add(quantified); } public String getQuantifier() { return quantifier; } public List getVariables() { return new ReadOnlyCollection(variables).ToList(); } public Sentence getQuantified() { return quantified; } // START-Sentence public String getSymbolicName() { return getQuantifier(); } public bool isCompound() { return true; } public List getArgs() { return new ReadOnlyCollection(args).ToList(); } public Object accept(FOLVisitor v, Object arg) { return v.visitQuantifiedSentence(this, arg); } public FOLNode copy() { List copyVars = new List(); foreach (Variable v in variables) { copyVars.Add((Variable)v.copy()); } return new QuantifiedSentence(quantifier, copyVars, quantified.copySentence()); } public Sentence copySentence() { return null; } // END-Sentence public override bool Equals(Object o) { if (this == o) { return true; } if ((o == null) || !(o is QuantifiedSentence)) { return false; } QuantifiedSentence cs = (QuantifiedSentence)o; return cs.quantifier.Equals(quantifier) && cs.variables.Equals(variables) && cs.quantified.Equals(quantified); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode = 37 * hashCode + quantifier.GetHashCode(); foreach (Variable v in variables) { hashCode = 37 * hashCode + v.GetHashCode(); } hashCode = hashCode * 37 + quantified.GetHashCode(); } return hashCode; } public override String ToString() { if (null == stringRep) { StringBuilder sb = new StringBuilder(); sb.Append(quantifier); sb.Append(" "); foreach (Variable v in variables) { sb.Append(v.ToString()); sb.Append(" "); } sb.Append(quantified.ToString()); stringRep = sb.ToString(); } return stringRep; } } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/Sentence.cs ================================================ using System.Collections.Generic; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface Sentence : FOLNode { Sentence copySentence(); } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/Term.cs ================================================ using System.Collections.Generic; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface Term : FOLNode { List getArgs(); Term copy(); } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/TermEquality.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Collections.ObjectModel; using aima.core.logic.fol.parsing; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class TermEquality : AtomicSentence { private Term term1, term2; private List terms = new List(); private String stringRep = null; private int hashCode = 0; public static String getEqualitySynbol() { return "="; } public TermEquality(Term term1, Term term2) { this.term1 = term1; this.term2 = term2; terms.Add(term1); terms.Add(term2); } public Term getTerm1() { return term1; } public Term getTerm2() { return term2; } // START-AtomicSentence public String getSymbolicName() { return getEqualitySynbol(); } public bool isCompound() { return true; } List AtomicSentence.getArgs() { return null; } AtomicSentence AtomicSentence.copy() { return null; } public List getArgs() { return new ReadOnlyCollection(terms).ToList(); } public Object accept(FOLVisitor v, Object arg) { return v.visitTermEquality(this, arg); } public FOLNode copy() { return new TermEquality((Term)term1.copy(), (Term)term2.copy()); } public Sentence copySentence() { return null; } // END-AtomicSentence public override bool Equals(Object o) { if (this == o) { return true; } if ((o == null) || !(o is TermEquality)) { return false; } TermEquality te = (TermEquality)o; return te.getTerm1().Equals(term1) && te.getTerm2().Equals(term2); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode = 37 * hashCode + getTerm1().GetHashCode(); hashCode = 37 * hashCode + getTerm2().GetHashCode(); } return hashCode; } public override String ToString() { if (null == stringRep) { StringBuilder sb = new StringBuilder(); sb.Append(term1.ToString()); sb.Append(" = "); sb.Append(term2.ToString()); stringRep = sb.ToString(); } return stringRep; } } } ================================================ FILE: aima-csharp/logic/fol/parsing/ast/Variable.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.fol.parsing; namespace aima.core.logic.fol.parsing.ast { /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class Variable : Term { private String value; private int hashCode = 0; private int indexical = -1; public Variable(String s) { value = s.Trim(); } public Variable(String s, int idx) { value = s.Trim(); indexical = idx; } public String getValue() { return value; } // START-Term public String getSymbolicName() { return getValue(); } public bool isCompound() { return false; } List FOLNode.getArgs() { return null; } public List getArgs() { // Is not Compound, therefore should // return null for its arguments return null; } public Object accept(FOLVisitor v, Object arg) { return v.visitVariable(this, arg); } FOLNode FOLNode.copy() { return copy(); } public Term copy() { return new Variable(value, indexical); } // END-Term public int getIndexical() { return indexical; } public void setIndexical(int idx) { indexical = idx; hashCode = 0; } public String getIndexedValue() { return value + indexical; } public override bool Equals(Object o) { if (this == o) { return true; } if (!(o is Variable)) { return false; } Variable v = (Variable)o; return v.getValue().Equals(getValue()) && v.getIndexical() == getIndexical(); } public override int GetHashCode() { if (0 == hashCode) { hashCode = 17; hashCode += indexical; hashCode = 37 * hashCode + value.GetHashCode(); } return hashCode; } public override String ToString() { return value; } } } ================================================ FILE: aima-csharp/logic/propositional/agent/KBAgent.cs ================================================ using aima.core.agent; using aima.core.agent.impl; using aima.core.logic.propositional.kb; using aima.core.logic.propositional.parsing.ast; namespace aima.core.logic.propositional.agent { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 7.1, page * 236.
*
* *
     * function KB-AGENT(percept) returns an action
     *   persistent: KB, a knowledge base
     *               t, a counter, initially 0, indicating time
     *           
     *   TELL(KB, MAKE-PERCEPT-SENTENCE(percept, t))
     *   action <- ASK(KB, MAKE-ACTION-QUERY(t))
     *   TELL(KB, MAKE-ACTION-SENTENCE(action, t))
     *   t <- t + 1
     *   return action
     * 
     * 
* * Figure 7.1 A generic knowledge-based agent. Given a percept, the agent adds * the percept to its knowledge base, asks the knowledge base for the best * action, and tells the knowledge base that it has in fact taken that action. * * @author Ciaran O'Reilly */ public abstract class KBAgent : AbstractAgent { // persistent: KB, a knowledge base protected KnowledgeBase KB; // t, a counter, initially 0, indicating time private int t = 0; public KBAgent(KnowledgeBase KB) { this.KB = KB; } // function KB-AGENT(percept) returns an action public override Action execute(Percept percept) { // TELL(KB, MAKE-PERCEPT-SENTENCE(percept, t)) KB.tell(makePerceptSentence(percept, t)); // action <- ASK(KB, MAKE-ACTION-QUERY(t)) Action action = ask(KB, makeActionQuery(t)); // TELL(KB, MAKE-ACTION-SENTENCE(action, t)) KB.tell(makeActionSentence(action, t)); // t <- t + 1 t = t + 1; // return action return action; } /** * MAKE-PERCEPT-SENTENCE constructs a sentence asserting that the agent * perceived the given percent at the given time. * * @param percept * the given percept * @param t * the given time * @return a sentence asserting that the agent perceived the given percept * at the given time. */ // MAKE-PERCEPT-SENTENCE(percept, t) public abstract Sentence makePerceptSentence(Percept percept, int t); /** * MAKE-ACTION-QUERY constructs a sentence that asks what action should be * done at the current time. * * @param t * the current time. * @return a sentence that asks what action should be done at the current * time. */ // MAKE-ACTION-QUERY(t) public abstract Sentence makeActionQuery(int t); /** * MAKE-ACTION-SENTENCE constructs a sentence asserting that the chosen action was executed. * @param action * the chose action. * @param t * the time at which the action was executed. * @return a sentence asserting that the chosen action was executed. */ // MAKE-ACTION-SENTENCE(action, t) public abstract Sentence makeActionSentence(Action action, int t); /** * A wrapper around the KB's ask() method which translates the action (in the form of * a sentence) determined by the KB into an allowed 'Action' object from the current * environment in which the KB-AGENT resides. * * @param KB * the KB to ask. * @param actionQuery * an action query. * @return the Action to be performed in response to the given query. */ // ASK(KB, MAKE-ACTION-QUERY(t)) public abstract Action ask(KnowledgeBase KB, Sentence actionQuery); } } ================================================ FILE: aima-csharp/logic/propositional/kb/KnowledgeBase.cs ================================================ using System; using aima.core.logic.propositional.parsing.ast; namespace aima.core.logic.propositional.kb { public class KnowledgeBase { internal void tell(Sentence sentence) { throw new NotImplementedException(); } } } ================================================ FILE: aima-csharp/logic/propositional/parsing/PLVisitor.cs ================================================ using System; using System.Collections.Generic; using aima.core.logic.propositional.parsing.ast; namespace aima.core.logic.propositional.parsing { /** * Propositional Logic Visitor: A Visitor Pattern/ for * traversing the abstract syntax tree structural representation of * propositional logic used in this library. The key difference between the * default Visitor pattern and the code here, is that in the former the visit() * methods have a void visit(ConcreteNode) signature while the visitors used * here have a Object visit(ConcreteNode, Object arg) signature. This simplifies * testing and allows some recursive code that is hard with the former . * * @author Ravi Mohan * @author Ciaran O'Reilly * * @param * the argument type to be passed to the visitor methods. * @param * the return type to be returned from the visitor methods. */ public interface PLVisitor { /** * Visit a proposition symbol (e.g A). * * @param sentence * a Sentence that is a propositional symbol. * @param arg * optional argument to be used by the visitor. * @return optional return value to be used by the visitor. */ R visitPropositionSymbol(PropositionSymbol sentence, A arg); /** * Visit a unary complex sentence (e.g. ~A). * * @param sentence * a Sentence that is a unary complex sentence. * @param arg * optional argument to be used by the visitor. * @return optional return value to be used by the visitor. */ R visitUnarySentence(ComplexSentence sentence, A arg); /** * Visit a binary complex sentence (e.g. A & B). * * @param sentence * a Sentence that is a binary complex sentence. * @param arg * optional argument to be used by the visitor. * @return optional return value to be used by the visitor. */ R visitBinarySentence(ComplexSentence sentence, A arg); } } ================================================ FILE: aima-csharp/logic/propositional/parsing/ast/AtomicSentence.cs ================================================ using System; namespace aima.core.logic.propositional.parsing.ast { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 244.
*
* The atomic sentences consist of a single proposition symbol. * * @author Ravi Mohan * @author Ciaran O'Reilly * */ public abstract class AtomicSentence : Sentence { } } ================================================ FILE: aima-csharp/logic/propositional/parsing/ast/ComplexSentence.cs ================================================ using aima.core.logic.propositional.parsing.ast; using System; namespace aima.core.logic.propositional.parsing.ast { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 244.
*
* Complex Sentence: are constructed from simpler sentences, using * parentheses (and square brackets) and logical connectives. * * @author Ciaran O'Reilly * @author Ravi Mohan */ public class ComplexSentence : Sentence { private Connective connective; private Sentence[] simplerSentences; // Lazy initialize these values. private int cachedHashCode = -1; private String cachedConcreteSyntax = null; /** * Constructor. * * @param connective * the complex sentence's connective. * @param sentences * the simpler sentences making up the complex sentence. */ public ComplexSentence(Connective connective, params Sentence[] sentences) { // Assertion checks assertLegalArguments(connective, sentences); this.connective = connective; simplerSentences = new Sentence[sentences.Length]; for (int i = 0; i < sentences.Length; i++) { simplerSentences[i] = sentences[i]; } } /** * Convenience constructor for binary sentences. * * @param sentenceL * the left hand sentence. * @param binaryConnective * the binary connective. * @param sentenceR * the right hand sentence. */ public ComplexSentence(Sentence sentenceL, Connective binaryConnective, Sentence sentenceR) : this(binaryConnective, sentenceL, sentenceR) { } public override Connective getConnective() { return connective; } public override int getNumberSimplerSentences() { return simplerSentences.Length; } public override Sentence getSimplerSentence(int offset) { return simplerSentences[offset]; } public override bool Equals(Object o) { if (this == o) { return true; } if ((o == null) || (this.GetType() != o.GetType())) { return false; } bool result = false; ComplexSentence other = (ComplexSentence)o; if (other.GetHashCode() == this.GetHashCode()) { if (other.getConnective().Equals(this.getConnective()) && other.getNumberSimplerSentences() == this .getNumberSimplerSentences()) { // connective and # of simpler sentences match // assume match and then test each simpler sentence result = true; for (int i = 0; i < this.getNumberSimplerSentences(); i++) { if (!other.getSimplerSentence(i).Equals( this.getSimplerSentence(i))) { result = false; break; } } } } return result; } public override int GetHashCode() { if (cachedHashCode == -1) { cachedHashCode = 17 * getConnective().GetHashCode(); foreach(Sentence s in simplerSentences) { cachedHashCode = (cachedHashCode * 37) + s.GetHashCode(); } } return cachedHashCode; } public override String ToString() { if (cachedConcreteSyntax == null) { if (isUnarySentence()) { cachedConcreteSyntax = getConnective() + bracketSentenceIfNecessary(getConnective(), getSimplerSentence(0)); } else if (isBinarySentence()) { cachedConcreteSyntax = bracketSentenceIfNecessary(getConnective(), getSimplerSentence(0)) + " " + getConnective() + " " + bracketSentenceIfNecessary(getConnective(), getSimplerSentence(1)); } } return cachedConcreteSyntax; } // // PRIVATE // private void assertLegalArguments(Connective connective, params Sentence[] sentences) { if (connective == null) { throw new ArgumentException("Connective must be specified."); } if (sentences == null) { throw new ArgumentException("> 0 simpler sentences must be specified."); } if (connective == Connective.NOT) { if (sentences.Length != 1) { throw new ArgumentException("A not (~) complex sentence only take 1 simpler sentence not " + sentences.Length); } } else { if (sentences.Length != 2) { throw new ArgumentException("Connective is binary (" + connective + ") but only " + sentences.Length + " simpler sentences provided"); } } } } } ================================================ FILE: aima-csharp/logic/propositional/parsing/ast/Connective.cs ================================================ using System; using aima.core.util; using System.Collections.Generic; namespace aima.core.logic.propositional.parsing.ast { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 244.
*
* *
    * Logical Connectives: There are five connectives in common use:
    * 1. ~   (not).
    * 2. &   (and).
    * 3. |   (or).
    * 4. =>  (implication).
    * 5. <=> (biconditional).
    * 
    * Note: We use ASCII characters that commonly have the same meaning to those 
    * symbols used in the book.
    * 
    * OPERATOR PRECEDENCE: ~, &, |, =>, <=>
    * 
* * @author Ciaran O'Reilly * @author Avinash Agarwal * */ public class Connective { public static readonly Connective NOT = new Connective("~", 10); public static readonly Connective AND = new Connective("&", 8); public static readonly Connective OR = new Connective("|", 6); public static readonly Connective IMPLICATION = new Connective("=>", 4); public static readonly Connective BICONDITIONAL = new Connective("<=>", 2); public static IEnumerable Values { get { yield return NOT; yield return AND; yield return OR; yield return IMPLICATION; yield return BICONDITIONAL; } } /** * * @return the symbol for this connective. */ public String getSymbol() { return symbol; } /** * * @return the precedence associated with this connective. */ public int getPrecedence() { return precedence; } public String toString() { return getSymbol(); } /** * Determine if a given symbol is representative of a connective. * * @param symbol * a symbol to be tested whether or not is represents a * connective. * @return true if the symbol passed in is representative of a connective. */ public static bool isConnective(String symbol) { if (NOT.getSymbol().Equals(symbol)) { return true; } else if (AND.getSymbol().Equals(symbol)) { return true; } else if (OR.getSymbol().Equals(symbol)) { return true; } else if (IMPLICATION.getSymbol().Equals(symbol)) { return true; } else if (BICONDITIONAL.getSymbol().Equals(symbol)) { return true; } return false; } /** * Get the connective associated with the given symbolic representation. * * @param symbol * a symbol for which a corresponding connective is wanted. * @return the connective associated with a given symbol. * @throws IllegalArgumentException * if a connective cannot be found that matches the given * symbol. */ public static Connective get(String symbol) { if (NOT.getSymbol().Equals(symbol)) { return NOT; } else if (AND.getSymbol().Equals(symbol)) { return AND; } else if (OR.getSymbol().Equals(symbol)) { return OR; } else if (IMPLICATION.getSymbol().Equals(symbol)) { return IMPLICATION; } else if (BICONDITIONAL.getSymbol().Equals(symbol)) { return BICONDITIONAL; } throw new ArgumentException( "Not a valid symbol for a connective: " + symbol); } /** * Determine if the given character is at the beginning of a connective. * * @param ch * a character. * @return true if the given character is at the beginning of a connective's * symbolic representation, false otherwise. */ public static bool isConnectiveIdentifierStart(char ch) { return _connectiveLeadingChars.Contains(ch); } /** * Determine if the given character is part of a connective. * * @param ch * a character. * @return true if the given character is part of a connective's symbolic * representation, false otherwise. */ public static bool isConnectiveIdentifierPart(char ch) { return _connectiveChars.Contains(ch); } // // PRIVATE // private static readonly HashSet _connectiveLeadingChars = Util.createSet('~', '&', '|', '=', '<'); private static readonly HashSet _connectiveChars = Util.createSet('~', '&', '|', '=', '<', '>'); private readonly int precedence; private readonly String symbol; private Connective(String symbol, int precedence) { this.precedence = precedence; this.symbol = symbol; } } } ================================================ FILE: aima-csharp/logic/propositional/parsing/ast/PropositionSymbol.cs ================================================ using System; using System.CodeDom.Compiler; namespace aima.core.logic.propositional.parsing.ast { /** * Artificial Intelligence A Modern Approach(3rd Edition): page 244.
*
* Proposition Symbol: Each such symbol stands for a proposition that can * be true or false. There are two proposition symbols with fixed meanings: * < i > True the always - true proposition and < i > False the always - false * proposition.< br > * < br > * < b > Note : While the book states:< br > * 'We use symbols that start with an upper case letter and may contain other * letters or subscripts'. In this implementation we allow any legal java * identifier to stand in for a proposition symbol. * * @author Ciaran O'Reilly * @author Ravi Mohan * * @see SourceVersion#isIdentifier(CharSequence) */ public class PropositionSymbol : AtomicSentence { public static readonly String TRUE_SYMBOL = "True"; public static readonly String FALSE_SYMBOL = "False"; public static readonly PropositionSymbol TRUE = new PropositionSymbol(TRUE_SYMBOL); public static readonly PropositionSymbol FALSE = new PropositionSymbol(FALSE_SYMBOL); // private String symbol; /** * Constructor. * * @param symbol * the symbol uniquely identifying the proposition. */ public PropositionSymbol(String symbol) { // Ensure differing cases for the 'True' and 'False' // propositional constants are represented in a canonical form. if (TRUE_SYMBOL.ToLower().Equals(symbol.ToLower())) { this.symbol = TRUE_SYMBOL; } else if (FALSE_SYMBOL.ToLower().Equals(symbol.ToLower())) { this.symbol = FALSE_SYMBOL; } else if (isPropositionSymbol(symbol)) { this.symbol = symbol; } else { throw new ArgumentException("Not a legal proposition symbol: " + symbol); } } /** * * @return true if this is the always 'True' proposition symbol, false * otherwise. */ public bool isAlwaysTrue() { return TRUE_SYMBOL.Equals(symbol); } /** * * @return true if the symbol passed in is the always 'True' proposition * symbol, false otherwise. */ public static bool isAlwaysTrueSymbol(String symbol) { return TRUE_SYMBOL.ToLower().Equals(symbol.ToLower()); } /** * * @return true if this is the always 'False' proposition symbol, false * other. */ public bool isAlwaysFalse() { return FALSE_SYMBOL.Equals(symbol); } /** * * @return true if the symbol passed in is the always 'False' proposition * symbol, false other. */ public static bool isAlwaysFalseSymbol(String symbol) { return FALSE_SYMBOL.ToLower().Equals(symbol.ToLower()); } /** * Determine if the given symbol is a legal proposition symbol. * * @param symbol * a symbol to be tested. * @return true if the given symbol is a legal proposition symbol, false * otherwise. */ public static bool isPropositionSymbol(String symbol) { CodeDomProvider provider = CodeDomProvider.CreateProvider("C#"); return provider.IsValidIdentifier(symbol); } /** * Determine if the given character can be at the beginning of a proposition * symbol. * * @param ch * a character. * @return true if the given character can be at the beginning of a * proposition symbol representation, false otherwise. */ public static bool isPropositionSymbolIdentifierStart(char ch) { CodeDomProvider provider = CodeDomProvider.CreateProvider("C#"); return provider.IsValidIdentifier(ch.ToString()); } /** * Determine if the given character is part of a proposition symbol. * * @param ch * a character. * @return true if the given character is part of a proposition symbols * representation, false otherwise. */ public static bool isPropositionSymbolIdentifierPart(char ch) { CodeDomProvider provider = CodeDomProvider.CreateProvider("C#"); return provider.IsValidIdentifier("a"+ch); } /** * * @return the symbol uniquely identifying the proposition. */ public String getSymbol() { return symbol; } public override bool Equals(Object o) { if (this == o) { return true; } if ((o == null) || (this.GetType() != o.GetType())) { return false; } PropositionSymbol sym = (PropositionSymbol)o; return symbol.Equals(sym.symbol); } public override int GetHashCode() { return symbol.GetHashCode(); } public override String ToString() { return getSymbol(); } } } ================================================ FILE: aima-csharp/logic/propositional/parsing/ast/Sentence.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using aima.core.logic.common; using aima.core.logic.propositional.parsing; using aima.core.util; namespace aima.core.logic.propositional.parsing.ast { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 244.
*
* The base of the knowledge representation language for propositional logic. * Note: this class hierarchy defines the abstract syntax representation used * for representing propositional logic. * * @author Ciaran O'Reilly * @author Ravi Mohan * @author Avinash Agarwal * */ public abstract class Sentence : ParseTreeNode { /** * * @return the logical connective associated with this sentence if it has * one (i.e. is a ComplexSentence), null otherwise. */ public virtual Connective getConnective() { return null; } /** * * @return the number of simpler sentences contained in this sentence. Will * only be > 0 if a Complex Sentence. */ public virtual int getNumberSimplerSentences() { return 0; } /** * Get the simplified sentence, at the specified offset (starts at 0), * contained by this Sentence if it is a Complex Sentence, null otherwise. * * @param offset * the offset of the contained simplified sentence to retrieve. * @return the simplified sentence, at the specified offset, contained by * this sentence (if a complex sentence), null otherwise. */ public virtual Sentence getSimplerSentence(int offset) { return null; } /** * * @return true if a complex sentence with a Not connective, false * otherwise. */ public bool isNotSentence() { return hasConnective(Connective.NOT); } /** * * @return true if a complex sentence with an And connective, false * otherwise. */ public bool isAndSentence() { return hasConnective(Connective.AND); } /** * * @return true if a complex sentence with an Or connective, false * otherwise. */ public bool isOrSentence() { return hasConnective(Connective.OR); } /** * * @return true if a complex sentence with an Implication connective, false * otherwise. */ public bool isImplicationSentence() { return hasConnective(Connective.IMPLICATION); } /** * * @return true if a complex sentence with a Biconditional connective, false * otherwise. */ public bool isBiconditionalSentence() { return hasConnective(Connective.BICONDITIONAL); } /** * * @return true if a proposition symbol, false otherwise. */ public bool isPropositionSymbol() { return getConnective() == null; } /** * * @return true if a complex sentence containing a single simpler sentence, * false otherwise. */ public bool isUnarySentence() { return hasConnective(Connective.NOT); } /** * * @return true if a complex sentence containing two simpler sentences, * false otherwise. */ public bool isBinarySentence() { return getConnective() != null && !hasConnective(Connective.NOT); } /** * Allow a PLVisitor to walk over the abstract syntax tree represented by this * Sentence. * * @param plv * a Propositional Logic visitor. * @param arg * an optional argument for use by the visior. * @return a result specific to the visitors behavior. */ public R accept(PLVisitor plv, A arg) { R result = default(R); if (isPropositionSymbol()) { result = plv.visitPropositionSymbol((PropositionSymbol)this, arg); } else if (isUnarySentence()) { result = plv.visitUnarySentence((ComplexSentence)this, arg); } else if (isBinarySentence()) { result = plv.visitBinarySentence((ComplexSentence)this, arg); } return result; } /** * Utility routine that will create a string representation of a given * Sentence and place it inside brackets if it is a complex sentence that * has lower precedence than this complex sentence.
*
* Note: this is a form of pretty printing, whereby we only add brackets in * the concrete syntax representation as needed to ensure it can be parsed * back again into an equivalent abstract syntax representation used here. * * @param parentConnective * the connective of the parent sentence. * @param childSentence * a simpler child sentence. * @return a String representation of the Sentence, bracketed if the parent * based on its connective has higher precedence. */ public String bracketSentenceIfNecessary(Connective parentConnective, Sentence childSentence) { String result = null; if (childSentence is ComplexSentence) { ComplexSentence cs = (ComplexSentence)childSentence; if (cs.getConnective().getPrecedence() < parentConnective .getPrecedence()) { result = "(" + childSentence + ")"; } } if (result == null) { result = childSentence.ToString(); } return result; } /** * Create a disjunction of disjuncts. * @param disjuncts * the disjuncts from which to create the disjunction. * @return a disjunction of the given disjuncts. */ public static Sentence newDisjunction(params Sentence[] disjuncts) { return newDisjunction(disjuncts.ToList()); } /** * Create a disjunction of disjuncts. * @param disjuncts * the disjuncts from which to create the disjunction. * @return a disjunction of the given disjuncts. */ public static Sentence newDisjunction(List disjuncts) { if (disjuncts.Count == 0) { return PropositionSymbol.FALSE; } else if (disjuncts.Count == 1) { return disjuncts[0]; } return new ComplexSentence(Util.first(disjuncts), Connective.OR, newDisjunction(Util.rest(disjuncts))); } /** * Create a conjunction of conjuncts. * @param conjuncts * the conjuncts from which to create the conjunction. * @return a conjunction of the given conjuncts. */ public static Sentence newConjunction(params Sentence[] conjuncts) { return newConjunction(conjuncts.ToList()); } /** * Create a conjunction of conjuncts. * @param conjuncts * the conjuncts from which to create the conjunction. * @return a conjunction of the given conjuncts. */ public static Sentence newConjunction(List conjuncts) { if (conjuncts.Count == 0) { return PropositionSymbol.TRUE; } else if (conjuncts.Count == 1) { return conjuncts[0]; } return new ComplexSentence(Util.first(conjuncts), Connective.AND, newConjunction(Util.rest(conjuncts))); } // PROTECTED protected bool hasConnective(Connective connective) { // Note: can use '==' as Connective is an enum. return getConnective() == connective; } } } ================================================ FILE: aima-csharp/search/Local/FitnessFunction.cs ================================================ namespace aima.core.search.local { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 127.
*
* Each state is rated by the objective function, or (in Genetic Algorithm * terminology) the fitness function. A fitness function should return higher * values for better states. *
* Here, we assume that all values are greater or equal to zero. * * @author Ciaran O'Reilly * * @param
* the type of the alphabet used in the representation of the * individuals in the population (this is to provide flexibility in * terms of how a problem can be encoded). */ public interface FitnessFunction { /** * * @param individual * the individual whose fitness is to be accessed. * @return the individual's fitness value (the higher the better). */ double apply(Individual individual); } } ================================================ FILE: aima-csharp/search/Local/Individual.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.search.local { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 127.
*
* A state in a genetic algorithm is represented as an individual from the * population. * * @author Ciaran O'Reilly * * @param
* the type of the alphabet used in the representation of the * individuals in the population (this is to provide flexibility in * terms of how a problem can be encoded). */ public class Individual { private List representation = new List(); private int descendants; // for debugging /** * Construct an individual using the provided representation. * * @param representation * the individual's representation. */ public Individual(List representation) { this.representation = representation; } /** * * @return the individual's representation. */ public List getRepresentation() { return representation; } /** * * @return the length of the individual's representation. */ public int length() { return representation.Count; } /** * Should be called by the genetic algorithm whenever the individual is * selected to produce a descendant. */ public void incDescendants() { descendants++; } // Returns the number of descendants for this individual. public int getDescendants() { return descendants; } public String toString() { return representation.ToString() + descendants; } } } ================================================ FILE: aima-csharp/search/Local/Scheduler.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.search.local { /** * @author Ravi Mohan * */ public class Scheduler { private int k, limit; private double lam; public Scheduler(int k, double lam, int limit) { this.k = k; this.lam = lam; this.limit = limit; } public Scheduler() { this.k = 20; this.lam = 0.045; this.limit = 100; } public double getTemp(int t) { if (t < limit) { double res = k * Math.Exp((-1) * lam * t); return res; } else { return 0.0; } } } } ================================================ FILE: aima-csharp/search/framework/CutOffIndicatorAction.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent.impl; namespace aima.core.search.framework { /** * A NoOp action that indicates a CutOff has occurred in a search. Used * primarily by DepthLimited and IterativeDeepening search routines. * * @author Ciaran O'Reilly */ public class CutOffIndicatorAction : DynamicAction { public static readonly CutOffIndicatorAction CUT_OFF = new CutOffIndicatorAction(); // START-Action public bool isNoOp() { return true; } // END-Action private CutOffIndicatorAction(): base("CutOff") { } } } ================================================ FILE: aima-csharp/search/framework/EvaluationFunction.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.search.framework { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 92.
*
* The evaluation function is construed as a cost estimate, so the node with the * lowest evaluation is expanded first. * * @author Ciaran O'Reilly * */ public interface EvaluationFunction { double f(Node n); } } ================================================ FILE: aima-csharp/search/framework/HeuristicFunction.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.search.framework { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 92.
*
* a heuristic function, denoted h(n):
* h(n) = estimated cost of the cheapest path from the state at node n to a goal * state.
*
* Notice that h(n) takes a node as input, but, unlike g(n) it depends only on * the state at that node. * * @author Ravi Mohan * */ public interface HeuristicFunction { double h(Object state); } } ================================================ FILE: aima-csharp/search/framework/Metrics.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.search.framework { /** * Stores key-value pairs for efficiency analysis. * * @author Ravi Mohan * @author Ruediger Lunde */ public class Metrics { private Dictionary hash; public Metrics() { this.hash = new Dictionary(); } public void set(String name, int i) { hash[name] = i.ToString(); } public void set(String name, double d) { hash[name] = d.ToString(); } public void incrementInt(String name) { set(name, getInt(name) + 1); } public void set(String name, long l) { hash[name] = l.ToString(); } public int getInt(String name) { return int.Parse(hash[name]); } public double getDouble(String name) { return double.Parse(hash[name]); } public long getLong(String name) { return long.Parse(hash[name]); } public String get(String name) { return hash[name]; } public HashSet keySet() { return new HashSet(hash.Keys); } public String toString() { SortedDictionary map = new SortedDictionary(hash); return map.ToString(); } } } ================================================ FILE: aima-csharp/search/framework/Node.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using Action = aima.core.agent.Action; namespace aima.core.search.framework { /// /// Represents a node in a search tree which corresponds to a state in a state space. /// /// /// Artificial Intelligence A Modern Approach (3rd Edition): Figure 3.10, page 79. /// - Nodes are the data structures from which the search tree is constructed. /// - Each node has a parent, a state, and various bookkeeping fields. /// - Arrows point from child to parent. /// /// @author Ravi Mohan /// @author Ciaran O'Reilly /// @author Mike Stampone /// public class Node { /// /// The action that was applied to the parent to generate this node. /// public Action Action { get; } /// /// The node in the search tree that generated this node. /// public Node Parent { get; } /// /// The cumulative cost, traditionally denoted by g(n), of the path from the initial state to this node (as indicated by the parent pointers). /// public double PathCost { get; } /// /// The state in the state space to which this node corresponds. /// public object State { get; } /// /// Creates a new instance of with the specified state. /// public Node(object state) { if (state == null) { throw new ArgumentNullException(nameof(state)); } State = state; PathCost = 0.0; } /// /// Creates a new instance of with the specified state, parent, action, and step cost. /// /// The step cost is the cost from the parent node to this node. /// /// public Node(object state, Node parent, Action action, double stepCost) : this(state) { if (parent == null) { throw new ArgumentNullException(nameof(parent)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } Parent = parent; Action = action; PathCost = parent.PathCost + stepCost; } /// /// Returns the path from the root node to this node. /// public List GetPathFromRoot() { var path = new List(); var current = this; while (true) { path.Insert(0, current); if (current.IsRoot()) { break; } current = current.Parent; } return path; } /// /// Returns if this node has no parent node; otherwise, . /// public bool IsRoot() { return Parent == null; } public override string ToString() { return string.Join(" <-- ", GetPathFromRoot().Select(x => $"[action={Action}, state={State}, pathCost={PathCost}]")); } } } ================================================ FILE: aima-csharp/search/framework/NodeExpander.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.search.framework.problem; namespace aima.core.search.framework { /** * Instances of this class are responsible for node creation and expansion. They * compute path costs, support progress tracing, and count the number of * {@link #expand(Node, Problem)} calls. * * @author Ruediger Lunde * */ public class NodeExpander { // expanding nodes public Node createRootNode(System.Object state) { return new Node(state); } /** * Computes the path cost for getting from the root node state via the * parent node state to the specified state, creates a new node for the * specified state, adds it as child of the provided parent, and returns it. */ public Node createNode(System.Object state, Node parent, Action action, double stepCost) { return new Node(state, parent, action, parent.PathCost + stepCost); } /** * Returns the children obtained from expanding the specified node in the * specified problem. * * @param node * the node to expand * @param problem * the problem the specified node is within. * * @return the children obtained from expanding the specified node in the * specified problem. */ public List expand(Node node, Problem problem) { List successors = new List(); ActionsFunction actionsFunction = problem.getActionsFunction(); ResultFunction resultFunction = problem.getResultFunction(); StepCostFunction stepCostFunction = problem.getStepCostFunction(); foreach (Action action in actionsFunction.actions(node.State)) { System.Object successorState = resultFunction.result(node.State, action); double stepCost = stepCostFunction.c(node.State, action, successorState); successors.Add(createNode(successorState, node, action, stepCost)); } foreach (NodeListener listener in nodeListeners) { listener.onNodeExpanded(node); } counter++; return successors; } // progress tracing and statistical data /** Interface for progress Tracers */ public interface NodeListener { void onNodeExpanded(Node node); } /** * All node listeners added to this list get informed whenever a node is * expanded. */ private List nodeListeners = new List(); /** Counts the number of {@link #expand(Node, Problem)} calls. */ private int counter; /** * Adds a listener to the list of node listeners. It is informed whenever a * node is expanded during search. */ public void addNodeListener(NodeListener listener) { nodeListeners.Add(listener); } /** * Resets the counter for {@link #expand(Node, Problem)} calls. */ public void resetCounter() { counter = 0; } /** * Returns the number of {@link #expand(Node, Problem)} calls since the last * counter reset. */ public int getNumOfExpandCalls() { return counter; } } } ================================================ FILE: aima-csharp/search/framework/PathCostFunction.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.search.framework { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 78.
*
* * @author Ciaran O'Reilly * */ public class PathCostFunction { public PathCostFunction() { } /** * * @param n * @return the cost, traditionally denoted by g(n), of the path from the * initial state to the node, as indicated by the parent pointers. */ public double g(Node n) { return n.PathCost; } } } ================================================ FILE: aima-csharp/search/framework/PerceptToStateFunction.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; namespace aima.core.search.framework { /** * This interface is to define how to Map a Percept to a State representation * for a problem solver within a specific environment. This arises in the * description of the Online Search algorithms from Chapter 4. * * @author Ciaran O'Reilly * */ public interface PerceptToStateFunction { /** * Get the problem state associated with a Percept. * * @param p * the percept to be transformed to a problem state. * @return a problem state derived from the Percept p. */ Object getState(Percept p); } } ================================================ FILE: aima-csharp/search/framework/Search.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.search.framework.problem; namespace aima.core.search.framework { /** * @author Ravi Mohan * @author Mike Stampone */ public interface Search { /** * Returns a list of actions to the goal if the goal was found, a list * containing a single NoOp Action if already at the goal, or an empty list * if the goal could not be found. * * @param p * the search problem * * @return a list of actions to the goal if the goal was found, a list * containing a single NoOp Action if already at the goal, or an * empty list if the goal could not be found. */ List search(Problem p); /** * Returns all the metrics of the search. * * @return all the metrics of the search. */ Metrics getMetrics(); } } ================================================ FILE: aima-csharp/search/framework/SearchAgent.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.search.framework.problem; namespace aima.core.search.framework { /** * @author Ravi Mohan * */ public class SearchAgent : AbstractAgent { protected List actionList; private List.Enumerator actionIterator; private Metrics searchMetrics; public SearchAgent(Problem p, Search search) { actionList = search.search(p); actionIterator = actionList.GetEnumerator(); searchMetrics = search.getMetrics(); } public override Action execute(Percept p) { if (actionIterator.MoveNext()) { return actionIterator.Current; } else { return NoOpAction.NO_OP; } } public bool isDone() { return null != actionIterator.Current; } public List getActions() { return actionList; } public Dictionary getInstrumentation() { Dictionary retVal = new Dictionary(); foreach (string key in searchMetrics.keySet()) { System.String value = searchMetrics.get(key); retVal.Add(key, value); } return retVal; } } } ================================================ FILE: aima-csharp/search/framework/SearchForActions.cs ================================================ using System; using System.Collections.Generic; using aima.core.agent; namespace aima.core.search.framework { /** * Interface for all search algorithms which store at least a part of the * exploration history as search tree and return a list of actions which lead * from the initial state to a goal state. * * @author Ruediger Lunde * */ public interface SearchForActions : Search { NodeExpander getNodeExpander(); } } ================================================ FILE: aima-csharp/search/framework/SearchUtils.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.search.framework.problem; namespace aima.core.search.framework { /** * Provides several useful static methods for implementing search. * * @author Ravi Mohan * @author Ruediger Lunde * */ public class SearchUtils { /** * Returns the list of actions corresponding to the complete path to the * given node or NoOp if path length is one. */ public static List getSequenceOfActions(Node node) { List nodes = node.GetPathFromRoot(); List actions = new List(); if(nodes.Count == 1) { // I'm at the root node, this indicates I started at the // Goal node, therefore just return a NoOp actions.Add(NoOpAction.NO_OP); } else { // ignore the root node this has no action // hence index starts from 1 not zero for (int i = 1; i < nodes.Count; i++) { Node node_temp = nodes[i]; actions.Add(node_temp.Action); } } return actions; } /** Returns an empty action list. */ public static List failure() { return new List(); } /** Checks whether a list of actions is empty. */ public static bool isFailure(List actions) { if(actions.Count == 0) { return true; } else { return false; } } /** * Calls the goal test of the problem and - if the goal test is effectively * a {@link SolutionChecker} - additionally checks, whether the solution is * acceptable. Solution checkers can be used to analyze several or all * solutions with only one search run. */ public static bool isGoalState(Problem p, Node n) { bool isGoal = false; GoalTest gt = p.getGoalTest(); if (gt.isGoalState(n.State)) { if (gt is SolutionChecker) { isGoal = ((SolutionChecker)gt).isAcceptableSolution( getSequenceOfActions(n), n.State); } else { isGoal = true; } } return isGoal; } } } ================================================ FILE: aima-csharp/search/framework/SimpleProblemSolvingAgent.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.util; using aima.core.search.framework.problem; namespace aima.core.search.framework { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 3.1, page 67.
*
* *
     * function SIMPLE-PROBLEM-SOLVING-AGENT(percept) returns an action
     *   persistent: seq, an action sequence, initially empty
     *               state, some description of the current world state
     *               goal, a goal, initially null
     *               problem, a problem formulation
     *           
     *   state <- UPDATE-STATE(state, percept)
     *   if seq is empty then
     *     goal    <- FORMULATE-GOAL(state)
     *     problem <- FORMULATE-PROBLEM(state, goal)
     *     seq     <- SEARCH(problem)
     *     if seq = failure then return a null action
     *   action <- FIRST(seq)
     *   seq <- REST(seq)
     *   return action
     * 
* * Figure 3.1 A simple problem-solving agent. It first formulates a goal and a * problem, searches for a sequence of actions that would solve the problem, and * then executes the actions one at a time. When this is complete, it formulates * another goal and starts over.
* * @author Ciaran O'Reilly * @author Mike Stampone */ public abstract class SimpleProblemSolvingAgent : AbstractAgent { // seq, an action sequence, initially empty private List seq = new List(); // private bool formulateGoalsIndefinitely = true; private int maxGoalsToFormulate = 1; private int goalsFormulated = 0; /** * Constructs a simple problem solving agent which will formulate goals * indefinitely. */ public SimpleProblemSolvingAgent() { formulateGoalsIndefinitely = true; } /** * Constructs a simple problem solving agent which will formulate, at * maximum, the specified number of goals. * * @param maxGoalsToFormulate * the maximum number of goals this agent is to formulate. */ public SimpleProblemSolvingAgent(int maxGoalsToFormulate) { formulateGoalsIndefinitely = false; this.maxGoalsToFormulate = maxGoalsToFormulate; } // function SIMPLE-PROBLEM-SOLVING-AGENT(percept) returns an action public override Action execute(Percept p) { Action action = NoOpAction.NO_OP; // state <- UPDATE-STATE(state, percept) updateState(p); // if seq is empty then do if (0 == seq.Count) { if (formulateGoalsIndefinitely || goalsFormulated < maxGoalsToFormulate) { if (goalsFormulated > 0) { notifyViewOfMetrics(); } // goal <- FORMULATE-GOAL(state) System.Object goal = formulateGoal(); goalsFormulated++; // problem <- FORMULATE-PROBLEM(state, goal) Problem problem = formulateProblem(goal); // seq <- SEARCH(problem) seq.AddRange(search(problem)); if (0 == seq.Count) { // Unable to identify a path seq.Add(NoOpAction.NO_OP); } } else { // Agent no longer wishes to // achieve any more goals setAlive(false); notifyViewOfMetrics(); } } if (seq.Count > 0) { // action <- FIRST(seq) action = Util.first(seq); // seq <- REST(seq) seq = Util.rest(seq); } return action; } // PROTECTED METHODS protected abstract State updateState(Percept p); protected abstract System.Object formulateGoal(); protected abstract Problem formulateProblem(System.Object goal); protected abstract List search(Problem problem); protected abstract void notifyViewOfMetrics(); } } ================================================ FILE: aima-csharp/search/framework/SolutionChecker.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.search.framework.problem; namespace aima.core.search.framework { /** * A specialization of the GoalTest interface so that it is possible to check * the solution once a Goal has been identified to determine if it is * acceptable. This allows you to continue searching for alternative solutions * without having to restart the search. * * However, care needs to be taken when doing this as it does not always make * sense to continue with a search once an initial goal is found, for example if * using a heuristic targeted at a single goal. * * @author Ciaran O'Reilly */ public interface SolutionChecker : GoalTest { /** * This method is only called if GoalTest.isGoalState() returns true. * * @param actions * the list of actions to get to the goal state. * * @param goal * the goal the list of actions will reach. * * @return true if the solution is acceptable, false otherwise, which * indicates the search should be continued. */ bool isAcceptableSolution(List actions, System.Object goal); } } ================================================ FILE: aima-csharp/search/framework/problem/ActionsFunction.cs ================================================ using System.Collections.Generic; using aima.core.agent; namespace aima.core.search.framework.problem { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 67.
*
* Given a particular state s, ACTIONS(s) returns the set of actions that can be * executed in s. We say that each of these actions is applicable in s. * * @author Ciaran O'Reilly * */ public interface ActionsFunction { /** * Given a particular state s, returns the set of actions that can be * executed in s. * * @param s * a particular state. * @return the set of actions that can be executed in s. */ HashSet actions(System.Object s); } } ================================================ FILE: aima-csharp/search/framework/problem/BidirectionalProblem.cs ================================================ using System.Collections.Generic; namespace aima.core.search.framework.problem { /** * An interface describing a problem that can be tackled from both directions at * once (i.e InitialState<->Goal). * * @author Ciaran O'Reilly * */ public interface BidirectionalProblem { Problem getOriginalProblem(); Problem getReverseProblem(); } } ================================================ FILE: aima-csharp/search/framework/problem/DefaultGoalTest.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.search.framework.problem { /** * Checks whether a given state equals an explicitly specified goal state. * * @author Ruediger Lunde */ public class DefaultGoalTest : GoalTest { private Object goalState; public DefaultGoalTest(Object goalState) { this.goalState = goalState; } public bool isGoalState(Object state) { return goalState.Equals(state); } } } ================================================ FILE: aima-csharp/search/framework/problem/DefaultStepCostFunction.cs ================================================ using System.Collections.Generic; using aima.core.agent; namespace aima.core.search.framework.problem { /** * Returns one for every action. * * @author Ravi Mohan */ public class DefaultStepCostFunction : StepCostFunction { public double c(System.Object stateFrom, Action action, System.Object stateTo) { return 1; } } } ================================================ FILE: aima-csharp/search/framework/problem/GoalTest.cs ================================================ using System.Collections.Generic; namespace aima.core.search.framework.problem { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 67.
*
* The goal test, which determines whether a given state is a goal state. * * @author Ravi Mohan * @author Mike Stampone */ public interface GoalTest { /** * Returns true if the given state is a goal state. * * @return true if the given state is a goal state. */ bool isGoalState(System.Object state); } } ================================================ FILE: aima-csharp/search/framework/problem/Problem.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.search.framework.problem { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 66.
*
* A problem can be defined formally by five components:
*
    *
  • The initial state that the agent starts in.
  • *
  • A description of the possible actions available to the agent. * Given a particular state s, ACTIONS(s) returns the set of actions that can be * executed in s.
  • *
  • A description of what each action does; the formal name for this is the * transition model, specified by a function RESULT(s, a) that returns the * state that results from doing action a in state s.
  • *
  • The goal test, which determines whether a given state is a goal * state.
  • *
  • A path cost function that assigns a numeric cost to each path. The * problem-solving agent chooses a cost function that reflects its own * performance measure. The step cost of taking action a in state s to * reach state s' is denoted by c(s,a,s')
  • *
* * @author Ravi Mohan * @author Ciaran O'Reilly * @author Mike Stampone */ public class Problem { protected Object initialState; protected ActionsFunction actionsFunction; protected ResultFunction resultFunction; protected GoalTest goalTest; protected StepCostFunction stepCostFunction; /** * Constructs a problem with the specified components, and a default step * cost function (i.e. 1 per step). * * @param initialState * the initial state that the agent starts in. * @param actionsFunction * a description of the possible actions available to the agent. * @param resultFunction * a description of what each action does; the formal name for * this is the transition model, specified by a function * RESULT(s, a) that returns the state that results from doing * action a in state s. * @param goalTest * test determines whether a given state is a goal state. */ public Problem(Object initialState, ActionsFunction actionsFunction, ResultFunction resultFunction, GoalTest goalTest) : this(initialState, actionsFunction, resultFunction, goalTest, new DefaultStepCostFunction()) { } /** * Constructs a problem with the specified components, which includes a step * cost function. * * @param initialState * the initial state of the agent. * @param actionsFunction * a description of the possible actions available to the agent. * @param resultFunction * a description of what each action does; the formal name for * this is the transition model, specified by a function * RESULT(s, a) that returns the state that results from doing * action a in state s. * @param goalTest * test determines whether a given state is a goal state. * @param stepCostFunction * a path cost function that assigns a numeric cost to each path. * The problem-solving-agent chooses a cost function that * reflects its own performance measure. */ public Problem(Object initialState, ActionsFunction actionsFunction, ResultFunction resultFunction, GoalTest goalTest, StepCostFunction stepCostFunction) { this.initialState = initialState; this.actionsFunction = actionsFunction; this.resultFunction = resultFunction; this.goalTest = goalTest; this.stepCostFunction = stepCostFunction; } /** * Returns the initial state of the agent. * * @return the initial state of the agent. */ public Object getInitialState() { return initialState; } /** * Returns true if the given state is a goal state. * * @return true if the given state is a goal state. */ public bool isGoalState(Object state) { return goalTest.isGoalState(state); } /** * Returns the goal test. * * @return the goal test. */ public GoalTest getGoalTest() { return goalTest; } /** * Returns the description of the possible actions available to the agent. * * @return the description of the possible actions available to the agent. */ public ActionsFunction getActionsFunction() { return actionsFunction; } /** * Returns the description of what each action does. * * @return the description of what each action does. */ public ResultFunction getResultFunction() { return resultFunction; } /** * Returns the path cost function. * * @return the path cost function. */ public StepCostFunction getStepCostFunction() { return stepCostFunction; } // PROTECTED METHODS protected Problem() { } } } ================================================ FILE: aima-csharp/search/framework/problem/ResultFunction.cs ================================================ using System.Collections.Generic; using aima.core.agent; namespace aima.core.search.framework.problem { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 67.
*
* A description of what each action does; the formal name for this is the * transition model, specified by a function RESULT(s, a) that returns the state * that results from doing action a in state s. We also use the term successor * to refer to any state reachable from a given state by a single action. * * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface ResultFunction { /** * Returns the state that results from doing action a in state s * * @param s * a particular state. * @param a * an action to be performed in state s. * @return the state that results from doing action a in state s. */ System.Object result(System.Object s, Action a); } } ================================================ FILE: aima-csharp/search/framework/problem/StepCostFunction.cs ================================================ using System.Collections.Generic; using aima.core.agent; namespace aima.core.search.framework.problem { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 68.
*
* The step cost of taking action a in state s to reach state s' is * denoted by c(s, a, s'). * * @author Ravi Mohan * @author Ciaran O'Reilly */ public interface StepCostFunction { /** * Calculate the step cost of taking action a in state s to reach state s'. * * @param s * the state from which action a is to be performed. * @param a * the action to be taken. * * @param sDelta * the state reached by taking the action. * @return the cost of taking action a in state s to reach state s'. */ double c(System.Object s, Action a, System.Object sDelta); } } ================================================ FILE: aima-csharp/search/framework/qsearch/GraphSearch.cs ================================================ using System.Collections.Generic; using System.Linq; using aima.core.agent; using aima.core.search.framework; using aima.core.search.framework.problem; namespace aima.core.search.framework.qsearch { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 3.7, page 77. *
* *
     * function GRAPH-SEARCH(problem) returns a solution, or failure
     *   initialize the frontier using the initial state of problem
     *   initialize the explored set to be empty
     *   loop do
     *     if the frontier is empty then return failure
     *     choose a leaf node and remove it from the frontier
     *     if the node contains a goal state then return the corresponding solution
     *     add the node to the explored set
     *     expand the chosen node, adding the resulting nodes to the frontier
     *       only if not in the frontier or explored set
     * 
* * Figure 3.7 An informal description of the general graph-search algorithm. *
* This implementation is based on the template method * {@link #search(Problem, Queue)} from superclass {@link QueueSearch} and * provides implementations for the needed primitive operations. In contrast to * the code above, here, nodes resulting from node expansion are added to the * frontier even if nodes with equal states already exist there. This makes it * possible to use the implementation also in combination with priority queue * frontiers. * * @author Ravi Mohan * @author Ciaran O'Reilly * @author Ruediger Lunde */ public class GraphSearch : QueueSearch { private HashSet explored = new HashSet(); public GraphSearch() : this(new NodeExpander()) { } public GraphSearch(NodeExpander nodeExpander): base(nodeExpander) { } /** * Clears the set of explored states and calls the search implementation of * QueSearch */ public override List search(Problem problem, Queue frontier) { // initialize the explored set to be empty explored.Clear(); // expandedNodes = new List(); return base.search(problem, frontier); } /** * Inserts the node at the tail of the frontier if the corresponding state * was not yet explored. */ protected override void addToFrontier(Node node) { if (!explored.Contains(node.State)) { frontier.Enqueue(node); updateMetrics(frontier.Count); } } /** * Removes the node at the head of the frontier, adds the corresponding * state to the explored set, and returns the node. As the template method * (the caller) calls {@link #isFrontierEmpty() before, the resulting node * state will always be unexplored yet. * * @return the node at the head of the frontier. */ protected override Node removeFromFrontier() { Node result = frontier.Dequeue(); // add the node to the explored set explored.Add(result.State); updateMetrics(frontier.Count); return result; } /** * Pops nodes of already explored states from the top end of the frontier * and checks whether there are still some nodes left. */ protected override bool isFrontierEmpty() { while (!(frontier.Count==0) && explored.Contains(frontier.Peek().State)) { frontier.Dequeue(); } updateMetrics(frontier.Count); if (frontier.Count == 0) { return true; } else return false; } } } ================================================ FILE: aima-csharp/search/framework/qsearch/GraphSearchBFS.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.search.framework; using aima.core.search.framework.problem; namespace aima.core.search.framework.qsearch { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 3.7, page 77. *
* *
     * function GRAPH-SEARCH(problem) returns a solution, or failure
     *   initialize the frontier using the initial state of problem
     *   initialize the explored set to be empty
     *   loop do
     *     if the frontier is empty then return failure
     *     choose a leaf node and remove it from the frontier
     *     if the node contains a goal state then return the corresponding solution
     *     add the node to the explored set
     *     expand the chosen node, adding the resulting nodes to the frontier
     *       only if not in the frontier or explored set
     * 
* * Figure 3.7 An informal description of the general graph-search algorithm. *
* This implementation is based on the template method * {@link #search(Problem, Queue)} from superclass {@link QueueSearch} and * provides implementations for the needed primitive operations. It is the most * efficient variant of graph search for breadth first search. But don't expect * shortest paths in combination with priority queue frontiers. * * @author Ravi Mohan * @author Ciaran O'Reilly * @author Ruediger Lunde */ public class GraphSearchBFS : QueueSearch { private HashSet explored = new HashSet(); private HashSet frontierStates = new HashSet(); public GraphSearchBFS() : this(new NodeExpander()) { } public GraphSearchBFS(NodeExpander nodeExpander) : base(nodeExpander) { } /** * Clears the set of explored states and calls the search implementation of * QueSearch */ public override List search(Problem problem, Queue frontier) { // Initialize the explored set to be empty explored.Clear(); frontierStates.Clear(); return base.search(problem, frontier); } /** * Inserts the node at the tail of the frontier if the corresponding state * is not already a frontier state and was not yet explored. */ protected override void addToFrontier(Node node) { if (!explored.Contains(node.State) && !frontierStates.Contains(node.State)) { frontier.Enqueue(node); frontierStates.Add(node.State); updateMetrics(frontier.Count); } } /** * Removes the node at the head of the frontier, adds the corresponding * state to the explored set, and returns the node. * * @return the node at the head of the frontier. */ protected override Node removeFromFrontier() { Node result = frontier.Dequeue(); explored.Add(result.State); frontierStates.Remove(result.State); updateMetrics(frontier.Count); return result; } /** * Checks whether there are still some nodes left. */ protected override bool isFrontierEmpty() { if (frontier.Count == 0) { return true; } else return false; } } } ================================================ FILE: aima-csharp/search/framework/qsearch/QueueSearch.cs ================================================ using System.Collections.Generic; using System.Threading; using aima.core.agent; using aima.core.util; using aima.core.search.framework.problem; namespace aima.core.search.framework.qsearch { /** * Base class for queue-based search implementations, especially for {@link TreeSearch}, * {@link GraphSearch}, and {@link BidirectionalSearch}. * * @author Ravi Mohan * @author Ciaran O'Reilly * @author Mike Stampone * @author Ruediger Lunde */ public abstract class QueueSearch { public const System.String METRIC_NODES_EXPANDED = "nodesExpanded"; public const System.String METRIC_QUEUE_SIZE = "queueSize"; public const System.String METRIC_MAX_QUEUE_SIZE = "maxQueueSize"; public const System.String METRIC_PATH_COST = "pathCost"; protected readonly NodeExpander nodeExpander; protected Queue frontier; protected bool earlyGoalCheck = false; protected Metrics metrics = new Metrics(); protected QueueSearch(NodeExpander nodeExpander) { this.nodeExpander = nodeExpander; } public virtual NodeExpander getNodeExpander() { return nodeExpander; } /** * Returns a list of actions to the goal if the goal was found, a list * containing a single NoOp Action if already at the goal, or an empty list * if the goal could not be found. This template method provides a base for * tree and graph search implementations. It can be customized by overriding * some primitive operations, especially {@link #addToFrontier(Node)}, * {@link #removeFromFrontier()}, and {@link #isFrontierEmpty()}. * * @param problem * the search problem * @param frontier * the collection of nodes that are waiting to be expanded * * @return a list of actions to the goal if the goal was found, a list * containing a single NoOp Action if already at the goal, or an * empty list if the goal could not be found. */ public virtual List search(Problem problem, Queue frontier) { this.frontier = frontier; clearInstrumentation(); // initialize the frontier using the initial state of the problem Node root = nodeExpander.createRootNode(problem.getInitialState()); if (earlyGoalCheck) { if(SearchUtils.isGoalState(problem, root)) { return getSolution(root); } } addToFrontier(root); while(!(frontier.Count == 0)) { // choose a leaf node and remove it from the frontier Node nodeToExpand = removeFromFrontier(); // Only need to check the nodeToExpand if have not already // checked before adding to the frontier if (!earlyGoalCheck) { // if the node contains a goal state then return the // corresponding solution if(SearchUtils.isGoalState(problem, nodeToExpand)) { return getSolution(nodeToExpand); } } // expand the chosen node, adding the resulting nodes to the // frontier foreach(Node successor in nodeExpander.expand(nodeToExpand, problem)) { if (earlyGoalCheck) { if(SearchUtils.isGoalState(problem, successor)) { return getSolution(successor); } } addToFrontier(successor) ; } } // if the frontier is empty then return failure return SearchUtils.failure(); } /** * Primitive operation which inserts the node at the tail of the frontier. */ protected abstract void addToFrontier(Node node); /** * Primitive operation which removes and returns the node at the head of the * frontier. * * @return the node at the head of the frontier. */ protected abstract Node removeFromFrontier(); /** * Primitive operation which checks whether the frontier contains not yet * expanded nodes. */ protected abstract bool isFrontierEmpty(); /** * Enables optimization for FIFO queue based search, especially breadth * first search. * * @param state */ public void setEarlyGoalCheck(bool state) { this.earlyGoalCheck = state; } /** * Returns all the search metrics. */ public virtual Metrics getMetrics() { metrics.set(METRIC_NODES_EXPANDED, nodeExpander.getNumOfExpandCalls()); return metrics; } /** * Sets all metrics to zero. */ public void clearInstrumentation() { nodeExpander.resetCounter(); metrics.set(METRIC_NODES_EXPANDED, 0); metrics.set(METRIC_QUEUE_SIZE, 0); metrics.set(METRIC_MAX_QUEUE_SIZE, 0); metrics.set(METRIC_PATH_COST, 0); } protected void updateMetrics(int queueSize) { metrics.set(METRIC_QUEUE_SIZE, queueSize); int maxQSize = metrics.getInt(METRIC_MAX_QUEUE_SIZE); if (queueSize > maxQSize) { metrics.set(METRIC_MAX_QUEUE_SIZE, queueSize); } } private List getSolution(Node node) { metrics.set(METRIC_PATH_COST, node.PathCost); return SearchUtils.getSequenceOfActions(node); } } } ================================================ FILE: aima-csharp/search/framework/qsearch/TreeSearch.cs ================================================ using System.Collections.Generic; using aima.core.search.framework; using aima.core.search.framework.problem; namespace aima.core.search.framework.qsearch { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 3.7, page 77. *
* *
     * function TREE-SEARCH(problem) returns a solution, or failure
     *   initialize the frontier using the initial state of the problem
     *   loop do
     *     if the frontier is empty then return failure
     *     choose a leaf node and remove it from the frontier
     *     if the node contains a goal state then return the corresponding solution
     *     expand the chosen node, adding the resulting nodes to the frontier
     * 
* * Figure 3.7 An informal description of the general tree-search algorithm. * *
* This implementation is based on the template method * {@link #search(Problem, Queue)} from superclass {@link QueueSearch} and * provides implementations for the needed primitive operations. * * @author Ravi Mohan * @author Ruediger Lunde * */ public class TreeSearch : QueueSearch { public TreeSearch(): this(new NodeExpander()) { } public TreeSearch(NodeExpander nodeExpander): base(nodeExpander) { } /** * Inserts the node at the tail of the frontier. */ protected override void addToFrontier(Node node) { frontier.Enqueue(node); updateMetrics(frontier.Count); } /** * Removes and returns the node at the head of the frontier. * * @return the node at the head of the frontier. */ protected override Node removeFromFrontier() { Node result = frontier.Dequeue(); updateMetrics(frontier.Count); return result; } /** * Checks whether the frontier contains not yet expanded nodes. */ protected override bool isFrontierEmpty() { if(frontier.Count == 0) { return true; } else return false; } } } ================================================ FILE: aima-csharp/search/online/LRTAStarAgent.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.search.framework; using aima.core.util; namespace aima.core.search.online { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 4.24, page * 152.
*
* *
     * function LRTA*-AGENT(s') returns an action
     *   inputs: s', a percept that identifies the current state
     *   persistent: result, a table, indexed by state and action, initially empty
     *               H, a table of cost estimates indexed by state, initially empty
     *               s, a, the previous state and action, initially null
     *           
     *   if GOAL-TEST(s') then return stop
     *   if s' is a new state (not in H) then H[s'] <- h(s')
     *   if s is not null
     *     result[s, a] <- s'
     *     H[s] <-        min LRTA*-COST(s, b, result[s, b], H)
     *             b (element of) ACTIONS(s)
     *   a <- an action b in ACTIONS(s') that minimizes LRTA*-COST(s', b, result[s', b], H)
     *   s <- s'
     *   return a
     *   
     * function LRTA*-COST(s, a, s', H) returns a cost estimate
     *   if s' is undefined then return h(s)
     *   else return c(s, a, s') + H[s']
     * 
* * Figure 4.24 LRTA*-AGENT selects an action according to the value of * neighboring states, which are updated as the agent moves about the state * space.
*
* Note: This algorithm fails to exit if the goal does not exist (e.g. * A<->B Goal=X), this could be an issue with the implementation. Comments * welcome. * * @author Ciaran O'Reilly * @author Mike Stampone */ public class LRTAStarAgent : AbstractAgent { private OnlineSearchProblem problem; private PerceptToStateFunction ptsFunction; private HeuristicFunction hf; // persistent: result, a table, indexed by state and action, initially empty private readonly TwoKeyHashMap result = new TwoKeyHashMap(); // H, a table of cost estimates indexed by state, initially empty private readonly Dictionary H = new Dictionary(); // s, a, the previous state and action, initially null private object s = null; private Action a = null; /** * Constructs a LRTA* agent with the specified search problem, percept to * state function, and heuristic function. * * @param problem * an online search problem for this agent to solve. * @param ptsFunction * a function which returns the problem state associated with a * given Percept. * @param hf * heuristic function h(n), which estimates the cost of * the cheapest path from the state at node n to a goal * state. */ public LRTAStarAgent(OnlineSearchProblem problem, PerceptToStateFunction ptsFunction, HeuristicFunction hf) { setProblem(problem); setPerceptToStateFunction(ptsFunction); setHeuristicFunction(hf); } /** * Returns the search problem of this agent. * * @return the search problem of this agent. */ public OnlineSearchProblem getProblem() { return problem; } /** * Sets the search problem for this agent to solve. * * @param problem * the search problem for this agent to solve. */ public void setProblem(OnlineSearchProblem problem) { this.problem = problem; init(); } /** * Returns the percept to state function of this agent. * * @return the percept to state function of this agent. */ public PerceptToStateFunction getPerceptToStateFunction() { return ptsFunction; } /** * Sets the percept to state function of this agent. * * @param ptsFunction * a function which returns the problem state associated with a * given Percept. */ public void setPerceptToStateFunction(PerceptToStateFunction ptsFunction) { this.ptsFunction = ptsFunction; } /** * Returns the heuristic function of this agent. */ public HeuristicFunction getHeuristicFunction() { return hf; } /** * Sets the heuristic function of this agent. * * @param hf * heuristic function h(n), which estimates the cost of * the cheapest path from the state at node n to a goal * state. */ public void setHeuristicFunction(HeuristicFunction hf) { this.hf = hf; } // function LRTA*-AGENT(s') returns an action // inputs: s', a percept that identifies the current state public override Action execute(Percept psDelta) { object sDelta = ptsFunction.getState(psDelta); // if GOAL-TEST(s') then return stop if(goalTest(sDelta)) { a = NoOpAction.NO_OP; } else { // if s' is a new state (not in H) then H[s'] <- h(s') if(!H.ContainsKey(sDelta)) { H.Add(sDelta, getHeuristicFunction().h(sDelta)); } // if s is not null if(null != s) { // result[s, a] <- s' result.put(s, a, sDelta); // H[s] <- min LRTA*-COST(s, b, result[s, b], H) // b (element of) ACTIONS(s) double minimum = double.MaxValue; foreach(Action b in actions(s)) { double cost = lrtaCost(s, b, result.get(s, b)); if(cost < minimum) { minimum = cost; } } H.Add(s, minimum); } // a <- an action b in ACTIONS(s') that minimizes LRTA*-COST(s', b, // result[s', b], H) double min = double.MaxValue; // Just in case no actions a = NoOpAction.NO_OP; foreach(agent.Action b in actions(sDelta)) { double cost = lrtaCost(sDelta, b, result.get(sDelta, b)); if(cost < min) { min = cost; a = b; } } } // s <- s' s = sDelta; if (a.isNoOp()) { // I'm either at the Goal or can't get to it, // which in either case I'm finished so just die. setAlive(false); } // return a return a; } // PRIVATE METHODS private void init() { setAlive(true); result.Clear(); H.Clear(); s = null; a = null; } private bool goalTest(object state) { return getProblem().isGoalState(state); } // function LRTA*-COST(s, a, s', H) returns a cost estimate private double lrtaCost(object s, Action action, object sDelta) { // if s' is undefined then return h(s) if (null == sDelta) { return getHeuristicFunction().h(s); } // else return c(s, a, s') + H[s'] return getProblem().getStepCostFunction().c(s, action, sDelta) + H[sDelta]; } private HashSet actions(object state) { return problem.getActionsFunction().actions(state); } } } ================================================ FILE: aima-csharp/search/online/OnlineDFSAgent.cs ================================================ using System.Collections.Generic; using aima.core.agent; using aima.core.agent.impl; using aima.core.search.framework; using aima.core.util; namespace aima.core.search.online { /** * Artificial Intelligence A Modern Approach (3rd Edition): Figure 4.21, page * 150.
*
* *
     * function ONLINE-DFS-AGENT(s') returns an action
     *   inputs: s', a percept that identifies the current state
     *   persistent: result, a table, indexed by state and action, initially empty
     *               untried, a table that lists, for each state, the actions not yet tried
     *               unbacktracked, a table that lists, for each state, the backtracks not yet tried
     *               s, a, the previous state and action, initially null
     *    
     *   if GOAL-TEST(s') then return stop
     *   if s' is a new state (not in untried) then untried[s'] <- ACTIONS(s')
     *   if s is not null then
     *       result[s, a] <- s'
     *       add s to the front of the unbacktracked[s']
     *   if untried[s'] is empty then
     *       if unbacktracked[s'] is empty then return stop
     *       else a <- an action b such that result[s', b] = POP(unbacktracked[s'])
     *   else a <- POP(untried[s'])
     *   s <- s'
     *   return a
     * 
* * Figure 4.21 An online search agent that uses depth-first exploration. The * agent is applicable only in state spaces in which every action can be * "undone" by some other action.
* * @author Ciaran O'Reilly * */ public class OnlineDFSAgent : AbstractAgent { private OnlineSearchProblem problem; private PerceptToStateFunction ptsFunction; private readonly TwoKeyHashMap result = new TwoKeyHashMap(); // untried, a table that lists, for each state, the actions not yet tried private readonly Dictionary> untried = new Dictionary>(); // unbacktracked, a table that lists, // for each state, the backtracks not yet tried private readonly Dictionary> unbacktracked = new Dictionary>(); // s, a, the previous state and action, initially null private object s = null; private Action a = null; /** * Constructs an online DFS agent with the specified search problem and * percept to state function. * * @param problem * an online search problem for this agent to solve * @param ptsFunction * a function which returns the problem state associated with a * given Percept. */ public OnlineDFSAgent(OnlineSearchProblem problem, PerceptToStateFunction ptsFunction) { setProblem(problem); setPerceptToStateFunction(ptsFunction); } /** * Returns the search problem for this agent. * * @return the search problem for this agent. */ public OnlineSearchProblem getProblem() { return problem; } /** * Sets the search problem for this agent to solve. * * @param problem * the search problem for this agent to solve. */ public void setProblem(OnlineSearchProblem problem) { this.problem = problem; init(); } /** * Returns the percept to state function of this agent. * * @return the percept to state function of this agent. */ public PerceptToStateFunction getPerceptToStateFunction() { return ptsFunction; } /** * Sets the percept to state functino of this agent. * * @param ptsFunction * a function which returns the problem state associated with a * given Percept. */ public void setPerceptToStateFunction(PerceptToStateFunction ptsFunction) { this.ptsFunction = ptsFunction; } // function ONLINE-DFS-AGENT(s') returns an action // inputs: s', a percept that identifies the current state public override Action execute(Percept psDelta) { object sDelta = ptsFunction.getState(psDelta); // if GOAL-TEST(s') then return stop if (goalTest(sDelta)) { a = NoOpAction.NO_OP; } else { // if s' is a new state (not in untried) then untried[s'] <- // ACTIONS(s') if (!untried.ContainsKey(sDelta)) { untried.Add(sDelta, actions(sDelta)); } // if s is not null then do if (null != s) { // Note: If I've already seen the result of this // [s, a] then don't put it back on the unbacktracked // list otherwise you can keep oscillating // between the same states endlessly. if (!(sDelta.Equals(result.get(s, a)))) { // result[s, a] <- s' result.put(s, a, sDelta); // Ensure the unbacktracked always has a list for s' if (!unbacktracked.ContainsKey(sDelta)) { unbacktracked.Add(sDelta, new List()); } // add s to the front of the unbacktracked[s'] unbacktracked[sDelta].Add(s); } } // if untried[s'] is empty then if (untried[sDelta].Capacity == 0) { // if unbacktracked[s'] is empty then return stop if (unbacktracked[sDelta].Capacity == 0) { a = NoOpAction.NO_OP; } else { // else a <- an action b such that result[s', b] = // POP(unbacktracked[s']) object popped = unbacktracked[sDelta].Remove(0); foreach(Pair sa in result.Keys) { if (sa.getFirst().Equals(sDelta) && result[sa].Equals(popped)) { a = sa.getSecond(); break; } } } } else //Needs debugging. { // else a <- POP(untried[s']) //a = untried[sDelta].Remove(0); } } if (a.isNoOp()) { // I'm either at the Goal or can't get to it, // which in either case I'm finished so just die. setAlive(false); } // s <- s' s = sDelta; // return a return a; } // PRIVATE METHODS private void init() { setAlive(true); result.Clear(); untried.Clear(); unbacktracked.Clear(); s = null; a = null; } private bool goalTest(object state) { return getProblem().isGoalState(state); } private List actions(object state) { return new List(problem.getActionsFunction() .actions(state)); } } } ================================================ FILE: aima-csharp/search/online/OnlineSearchProblem.cs ================================================ using System; using System.Collections.Generic; using aima.core.search.framework; using aima.core.search.framework.problem; namespace aima.core.search.online { /** * Artificial Intelligence A Modern Approach (3rd Edition): page 147.
*
* An online search problem must be solved by an agent executing actions, rather * than by pure computation. We assume a deterministic and fully observable * environment (Chapter 17 relaxes these assumptions), but we stipulate that the * agent knows only the following:
*
    *
  • ACTIONS(s), which returns a list of actions allowed in state s;
  • *
  • The step-cost function c(s, a, s') - note that this cannot be used until * the agent knows that s' is the outcome; and
  • *
  • GOAL-TEST(s).
  • *
* * @author Ciaran O'Reilly * @author Mike Stampone */ public class OnlineSearchProblem { protected ActionsFunction actionsFunction; protected StepCostFunction stepCostFunction; protected GoalTest goalTest; /** * Constructs an online search problem with the specified action function, * goal test, and a default step cost function. * * @param actionsFunction * ACTIONS(s), which returns a list of actions allowed in state s * @param goalTest * GOAL-TEST(s), which the agent can apply to a single state * description to determine if it is a goal state */ public OnlineSearchProblem(ActionsFunction actionsFunction, GoalTest goalTest) { this.actionsFunction = actionsFunction; this.goalTest = goalTest; this.stepCostFunction = new DefaultStepCostFunction(); } /** * Constructs an online search problem with the specified action function, * goal test, and a default step cost function. * * @param actionsFunction * ACTIONS(s), which returns a list of actions allowed in state s * @param goalTest * GOAL-TEST(s), which the agent can apply to a single state * description to determine if it is a goal state * @param stepCostFunction * the step-cost function c(s, a, s') - note that this cannot be * used until the agent knows that s' is the outcome */ public OnlineSearchProblem(ActionsFunction actionsFunction, GoalTest goalTest, StepCostFunction stepCostFunction) { this.actionsFunction = actionsFunction; this.goalTest = goalTest; this.stepCostFunction = stepCostFunction; } /** * Returns the action function of this online search problem. * * @return the action function of this online search problem. */ public ActionsFunction getActionsFunction() { return actionsFunction; } /** * Returns true if the given state is a goal state. * * @param state * an object representing a state * * @return true if the given state is a goal state. */ public bool isGoalState(Object state) { return goalTest.isGoalState(state); } /** * Returns the step cost function of this online search problem. * * @return the step cost function of this online search problem. */ public StepCostFunction getStepCostFunction() { return stepCostFunction; } // PROTECTED METHODS protected OnlineSearchProblem() { } } } ================================================ FILE: aima-csharp/util/ArrayIterator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; namespace aima.core.util { /** * Iterates efficiently through an array. * * @author Ruediger Lunde */ public class ArrayIterator : IEnumerator { T[] values; int counter; public T Current { get { return values[counter]; } } object IEnumerator.Current { get { return Current; } } public ArrayIterator(T[] values) { this.values = values; counter = 0; } public bool hasNext() { return counter < values.Length; } public T next() { return values[counter++]; } public void remove() { throw new NotSupportedException(); } public bool MoveNext() { counter++; return (counter < values.Length); } public void Dispose() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } } } ================================================ FILE: aima-csharp/util/CSharpRandomizer.cs ================================================ using System.Collections.Generic; using System; namespace aima.core.util { /** * Implementation of the Randomizer Interface using Java's java.util.Random * class. * * @author Ravi Mohan * */ public class CSharpRandomizer : Randomizer { private static Random _r = new Random(); private Random r = null; public CSharpRandomizer(): this(_r) { } public CSharpRandomizer(Random r) { this.r = r; } // START-Randomizer public double nextDouble() { return r.NextDouble(); } // END-Randomizer } } ================================================ FILE: aima-csharp/util/DisjointSets.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace aima.core.util { /** * A basic implementation of a disjoint-set data structure for maintaining a * collection of disjoint dynamic sets. This is based on the algorithm * description in Chapter 21 of 'Introduction to Algorithm 2nd Edition' (by * Cormen, Leriserson, Rivest, and Stein) for Disjoint-sets taking into account * some of the heuristic ideas described. However, this implementation relies on * the constant time performance of the HashMap's get and put operations, and * HashSet's add, remove, contains and size operations as an alternative * implementation approach. This provides a cleaner separation between the * elements and the implementation (i.e. the idea of a representative for a * particular disjoint set is not used) and an easier to use API (i.e. direct * access to the disjoint set that an element belongs to and no need to * understand how the internals work with respect to a representative) but will * not perform as fast (proper analysis required but likely O(m + n lg n) as * detailed in Theorem 21.1 on page 504 of 'Introduction to Algorithm 2nd * Edition'). * * Note: the internal implementation of this class can likely be improved to use * all the techniques outlined in section 21.3 in order to get optimal * performance. * * @author Ciaran O'Reilly * * @param * the type of elements to be contained by the disjoint sets. */ public class DisjointSets { private Dictionary> elementToSet = new Dictionary>(); private LinkedHashSet> disjointSets = new LinkedHashSet>(); /** * Default Constructor. */ public DisjointSets() { } /** * Constructor. * * @param initialElements * a collection of elements, each of which will be assigned to * their own disjoint set via makeSet(). */ public DisjointSets(ICollection initialElements) { foreach (E element in initialElements) { makeSet(element); } } /** * Constructor. * * @param initialElements * a collection of elements, each of which will be assigned to * their own disjoint set via makeSet(). */ public DisjointSets(params E[] elements) { foreach (E element in elements) { makeSet(element); } } /** * Create a disjoint set for the element passed in. This method should be * called for all elements before any of the other API methods are called * (i.e. construct a disjoint set for each element first and then union them * together as appropriate). * * @param element * the element for which a new disjoint set should be * constructed. */ public void makeSet(E element) { if (!elementToSet.ContainsKey(element)) { // Note: It is necessary to use an identity based hash set // whose equal and hashCode method are based on the Sets // identity and not its elements as we are adding // this set to a set but changing its values as unions // occur. IdentityHashSet set = new IdentityHashSet(); set.Add(element); elementToSet.Add(element, set); disjointSets.Add(set); } } /** * Union two disjoint sets together if the arguments currently belong to two * different sets. * * @param element1 * @param element2 * @throws IllegalArgumentException * if element1 or element 2 is not already associated with a * disjoint set (i.e. makeSet() was not called for the argument * beforehand). */ public void union(E element1, E element2) { HashSet set1 = elementToSet[element1]; if (set1 == null) { throw new ArgumentException( "element 1 is not associated with a disjoint set, call makeSet() first."); } HashSet set2 = elementToSet[element2]; if (set2 == null) { throw new ArgumentException( "element 2 is not associated with a disjoint set, call makeSet() first."); } if (set1 != set2) { // simple weighted union heuristic if (set1.Count < set2.Count) { set2.Union(set1); foreach (E element in set1) { elementToSet.Add(element, set2); disjointSets.Remove(set2); } } else { // i.e. set1 >= set2 set1.Union(set2); foreach (E element in set2) { elementToSet.Add(element, set1); disjointSets.Remove(set1); } } } } /** * Find the disjoint set that an element belongs to. * * @param element * the element whose disjoint set is being sought. * @return the disjoint set for the element or null if makeSet(element) was * not previously called. */ public LinkedHashSet find(E element) { // Note: Instantiate normal sets to ensure IdentityHashSet // is not exposed outside of this class. // This also ensures the internal logic cannot // be corrupted externally due to changing sets. return new LinkedHashSet(elementToSet[element]); } /** * * @return a map for each element and the corresponding disjoint set that it * belongs to. */ public Dictionary> getElementToDisjointSet() { // Note: Instantiate normal sets to ensure IdentityHashSet // is not exposed outside of this class. // This also ensures the internal logic cannot // be corrupted externally due to changing sets. Dictionary> result = new Dictionary>(); foreach (KeyValuePair> entry in elementToSet) { result.Add(entry.Key, new HashSet(entry.Value)); } return result; } /** * * @return the set of disjoint sets being maintained. */ public HashSet> getDisjointSets() { // Note: Instantiate normal sets to ensure IdentityHashSet // is not exposed outside of this class. // This also ensures the internal logic cannot // be corrupted externally due to changing sets. HashSet> result = new HashSet>(); IEnumerator> it = disjointSets.GetEnumerator(); while (it.MoveNext()) { result.Add(new HashSet(it.Current)); } return result; } /** * * @return the number of disjoint sets. */ public int numberDisjointSets() { return disjointSets.Count; } /** * Remove all the disjoint sets. */ public void clear() { elementToSet.Clear(); disjointSets.Clear(); } // PRIVATE METHODS // Override hashCode and equals so that // the Set itself and not its elements // are used to determine its hashCode // and equality. private class IdentityHashSet : HashSet { private static readonly long serialVersionUID = 1L; public int hashCode() { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } public bool equals(Object o) { return this == o; } } } } ================================================ FILE: aima-csharp/util/FrequencyCounter.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * A utility class for keeping counts of objects. Will return 0 for any object * for which it has not recorded a count against. * * @author Ravi Mohan * @author Mike Stampone */ public class FrequencyCounter { private Dictionary counter; private int total; /** * Default Constructor. */ public FrequencyCounter() { counter = new Dictionary(); total = 0; } /** * Returns the count to which the specified key is mapped in this frequency * counter, or 0 if the map contains no mapping for this key. * * @param key * the key whose associated count is to be returned. * * @return the count to which this map maps the specified key, or 0 if the * map contains no mapping for this key. */ public int getCount(T key) { int value = counter[key]; if (value == null) { return 0; } return value; } /** * Increments the count to which the specified key is mapped in this * frequency counter, or puts 1 if the map contains no mapping for this key. * * @param key * the key whose associated count is to be returned. */ public void incrementFor(T key) { int value = counter[key]; if(value == null) { counter.Add(key, 1); } else { counter.Add(key, value + 1); } // Keep track of the total total++; } /** * Returns the count to which the specified key is mapped in this frequency * counter, divided by the total of all counts. * * @param key * the key whose associated count is to be divided. * * @return the count to which this map maps the specified key, divided by * the total count. */ public Double probabilityOf(T key) { int value = getCount(key); if (value == 0) { return 0.0; } else { Double total = 0.0; foreach (T k in counter.Keys) { total += getCount(k); } return value / total; } } /** * * @return a set of objects for which frequency counts have been recorded. */ public HashSet getStates() { return new HashSet(counter.Keys); } /** * Remove all the currently recorded frequency counts. */ public void clear() { counter.Clear(); total = 0; } public String toString() { return counter.ToString(); } } } ================================================ FILE: aima-csharp/util/Interval.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * Basic supports for Intervals. * * @see Interval * * @author Ciaran O'Reilly * @author Mike Stampone */ public class Interval { private IComparable lower = null; private bool lowerInclusive = true; private IComparable upper = null; private bool upperInclusive = true; public Interval() { } /** * Constructs a closed interval from the two specified end points. * * @param lower * the lower end point of the interval * @param upper * the upper end point of the interval */ public Interval(IComparable lower, IComparable upper) { setLower(lower); setUpper(upper); } /** * Constructs an interval from the two specified end points. * * @param lower * the lower end point of the interval * @param lowerInclusive * wether or not the lower end of the interval is inclusive of * its value. * @param upper * the upper end point of the interval * @param upperInclusive * whether or not the upper end of the interval is inclusive of * its value. */ public Interval(IComparable lower, bool lowerInclusive, IComparable upper, bool upperInclusive) { setLower(lower); setLowerInclusive(lowerInclusive); setUpper(upper); setUpperInclusive(upperInclusive); } /** * Returns true if the specified object is between the end * points of this interval. * * @return true if the specified value is between the end * points of this interval. */ public bool isInInterval(C o) { if(null == lower || null == upper) { return false; } bool In = true; if (isLowerInclusive()) { In = lower.CompareTo(o) <= 0; } else { In = lower.CompareTo(o) < 0; } if (In) { if (isUpperInclusive()) { In = upper.CompareTo(o) >= 0; } else { In = upper.CompareTo(o) > 0; } } return In; } /** * Returns true if this interval is lower inclusive. * * @return true if this interval is lower inclusive. */ public bool isLowerInclusive() { return lowerInclusive; } /** * Returns true if this interval is not lower inclusive. * * @return true if this interval is not lower inclusive. */ public bool isLowerExclusive() { return !lowerInclusive; } /** * Sets the interval to lower inclusive or lower exclusive. * * @param inclusive * true represents lower inclusive and * false represents lower exclusive. */ public void setLowerInclusive(bool inclusive) { this.lowerInclusive = inclusive; } /** * Sets the interval to lower exclusive or lower inclusive. * * @param exclusive * true represents lower exclusive and * false represents lower inclusive. */ public void setLowerExclusive(bool exclusive) { this.lowerInclusive = !exclusive; } /** * Returns the lower end point of the interval. * * @return the lower end point of the interval. */ public IComparable getLower() { return lower; } /** * Sets the lower end point of the interval. * * @param lower * the lower end point of the interval */ public void setLower(IComparable lower) { this.lower = lower; } /** * Returns true if this interval is upper inclusive. * * @return true if this interval is upper inclusive. */ public bool isUpperInclusive() { return upperInclusive; } /** * Returns true if this interval is upper exclusive. * * @return true if this interval is upper exclusive. */ public bool isUpperExclusive() { return !upperInclusive; } /** * Sets the interval to upper inclusive or upper exclusive. * * @param inclusive * true represents upper inclusive and * false represents upper exclusive. */ public void setUpperInclusive(bool inclusive) { this.upperInclusive = inclusive; } /** * Sets the interval to upper exclusive or upper inclusive. * * @param exclusive * true represents upper exclusive and * false represents upper inclusive. */ public void setUpperExclusive(bool exclusive) { this.upperInclusive = !exclusive; } /** * Returns the upper end point of the interval. * * @return the upper end point of the interval. */ public IComparable getUpper() { return upper; } /** * Sets the upper end point of the interval. * * @param upper * the upper end point of the interval. */ public void setUpper(IComparable upper) { this.upper = upper; } public System.String toString() { return (lowerInclusive ? "[" : "(") + lower + ", " + upper + (upperInclusive ? "]" : ")"); } } } ================================================ FILE: aima-csharp/util/LUDecomposition.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * LU Decomposition. *

* For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n unit * lower triangular matrix L, an n-by-n upper triangular matrix U, and a * permutation vector piv of length m so that A(piv,:) = L*U. If m < n, then L * is m-by-m and U is m-by-n. *

* The LU decompostion with pivoting always exists, even if the matrix is * singular, so the constructor will never fail. The primary use of the LU * decomposition is in the solution of square systems of simultaneous linear * equations. This will fail if isNonsingular() returns false. */ public class LUDecomposition { private const long serialVersionUID = 1L; /* * ------------------------ Class variables ------------------------ */ /** * Array for internal storage of decomposition. * * @serial internal array storage. */ private double[][] LU; /** * Row and column dimensions, and pivot sign. * * @serial column dimension. * @serial row dimension. * @serial pivot sign. */ private int m, n; private int pivsign; /** * Internal storage of pivot vector. * * @serial pivot vector. */ private int[] piv; /* * ------------------------ Constructor ------------------------ */ /** * LU Decomposition, a structure to access L, U and piv. * * @param A * Rectangular matrix */ public LUDecomposition(Matrix A) { // Use a "left-looking", dot-product, Crout/Doolittle algorithm. LU = A.getArrayCopy(); m = A.getRowDimension(); n = A.getColumnDimension(); piv = new int[m]; for (int i = 0; i < m; i++) { piv[i] = i; } pivsign = 1; double[] LUrowi; double[] LUcolj = new double[m]; // Outer loop. for (int j = 0; j < n; j++) { // Make a copy of the j-th column to localize references. for (int i = 0; i < m; i++) { LUcolj[i] = LU[i][j]; } // Apply previous transformations. for (int i = 0; i < m; i++) { LUrowi = LU[i]; // Most of the time is spent in the following dot product. int kmax = Math.Min(i, j); double s = 0.0; for (int k = 0; k < kmax; k++) { s += LUrowi[k] * LUcolj[k]; } LUrowi[j] = LUcolj[i] -= s; } // Find pivot and exchange if necessary. int p = j; for (int i = j + 1; i < m; i++) { if (Math.Abs(LUcolj[i]) > Math.Abs(LUcolj[p])) { p = i; } } if (p != j) { for (int k = 0; k < n; k++) { double t = LU[p][k]; LU[p][k] = LU[j][k]; LU[j][k] = t; } int k2 = piv[p]; piv[p] = piv[j]; piv[j] = k2; pivsign = -pivsign; } // Compute multipliers. if (j < m & LU[j][j] != 0.0) { for (int i = j + 1; i < m; i++) { LU[i][j] /= LU[j][j]; } } } } /* * ------------------------ Temporary, experimental code. * ------------------------\ * * \ LU Decomposition, computed by Gaussian elimination.

This * constructor computes L and U with the "daxpy"-based elimination algorithm * used in LINPACK and MATLAB. In Java, we suspect the dot-product, Crout * algorithm will be faster. We have temporarily included this constructor * until timing experiments confirm this suspicion.

@param A Rectangular * matrix @param linpackflag Use Gaussian elimination. Actual value ignored. * * @return Structure to access L, U and piv. \ * * public LUDecomposition (Matrix A, int linpackflag) { // Initialize. LU = * A.getArrayCopy(); m = A.getRowDimension(); n = A.getColumnDimension(); * piv = new int[m]; for (int i = 0; i < m; i++) { piv[i] = i; } pivsign = * 1; // Main loop. for (int k = 0; k < n; k++) { // Find pivot. int p = k; * for (int i = k+1; i < m; i++) { if (Math.abs(LU[i][k]) > * Math.abs(LU[p][k])) { p = i; } } // Exchange if necessary. if (p != k) { * for (int j = 0; j < n; j++) { double t = LU[p][j]; LU[p][j] = LU[k][j]; * LU[k][j] = t; } int t = piv[p]; piv[p] = piv[k]; piv[k] = t; pivsign = * -pivsign; } // Compute multipliers and eliminate k-th column. if * (LU[k][k] != 0.0) { for (int i = k+1; i < m; i++) { LU[i][k] /= LU[k][k]; * for (int j = k+1; j < n; j++) { LU[i][j] -= LU[i][k]LU[k][j]; } } } } } \ * ------------------------ End of temporary code. ------------------------ */ /* * ------------------------ Public Methods ------------------------ */ /** * Is the matrix nonsingular? * * @return true if U, and hence A, is nonsingular. */ public bool isNonsingular() { for (int j = 0; j < n; j++) { if (LU[j][j] == 0) return false; } return true; } /** * Return lower triangular factor * * @return L */ public Matrix getL() { Matrix X = new Matrix(m, n); double[][] L = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (i > j) { L[i][j] = LU[i][j]; } else if (i == j) { L[i][j] = 1.0; } else { L[i][j] = 0.0; } } } return X; } /** * Return upper triangular factor * * @return U */ public Matrix getU() { Matrix X = new Matrix(n, n); double[][] U = X.getArray(); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i <= j) { U[i][j] = LU[i][j]; } else { U[i][j] = 0.0; } } } return X; } /** * Return pivot permutation vector * * @return piv */ public int[] getPivot() { int[] p = new int[m]; for (int i = 0; i < m; i++) { p[i] = piv[i]; } return p; } /** * Return pivot permutation vector as a one-dimensional double array * * @return (double) piv */ public double[] getDoublePivot() { double[] vals = new double[m]; for (int i = 0; i < m; i++) { vals[i] = piv[i]; } return vals; } /** * Determinant * * @return det(A) * @exception IllegalArgumentException * Matrix must be square */ public double det() { if (m != n) { throw new ArgumentException("Matrix must be square."); } double d = pivsign; for (int j = 0; j < n; j++) { d *= LU[j][j]; } return d; } /** * Solve A*X = B * * @param B * A Matrix with as many rows as A and any number of columns. * @return X so that L*U*X = B(piv,:) * @exception IllegalArgumentException * Matrix row dimensions must agree. * @exception ApplicationException * Matrix is singular. */ public Matrix solve(Matrix B) { if (B.getRowDimension() != m) { throw new ArgumentException( "Matrix row dimensions must agree."); } if (!this.isNonsingular()) { throw new ApplicationException("Matrix is singular."); } // Copy right hand side with pivoting int nx = B.getColumnDimension(); Matrix Xmat = B.getMatrix(piv, 0, nx - 1); double[][] X = Xmat.getArray(); // Solve L*Y = B(piv,:) for (int k = 0; k < n; k++) { for (int i = k + 1; i < n; i++) { for (int j = 0; j < nx; j++) { X[i][j] -= X[k][j] * LU[i][k]; } } } // Solve U*X = Y; for (int k = n - 1; k >= 0; k--) { for (int j = 0; j < nx; j++) { X[k][j] /= LU[k][k]; } for (int i = 0; i < k; i++) { for (int j = 0; j < nx; j++) { X[i][j] -= X[k][j] * LU[i][k]; } } } return Xmat; } } } ================================================ FILE: aima-csharp/util/LabeledGraph.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * Represents a directed labeled graph. Vertices are represented by their unique * labels and labeled edges by means of nested hashtables. Variant of class * {@code aima.util.Table}. This version is more dynamic, it requires no * initialization and can add new items whenever needed. * * @author R. Lunde * @author Mike Stampone */ public class LabeledGraph { /** * Lookup for edge label information. Contains an entry for every vertex * label. */ private readonly Dictionary> globalEdgeLookup; /** List of the labels of all vertices within the graph. */ private readonly List vertexLabels; /** Creates a new empty graph. */ public LabeledGraph() { globalEdgeLookup = new Dictionary>(); vertexLabels = new List(); } /** * Adds a new vertex to the graph if it is not already present. * * @param v * the vertex to add */ public void addVertex(VertexLabelType v) { checkForNewVertex(v); } /** * Adds a directed labeled edge to the graph. The end points of the edge are * specified by vertex labels. New vertices are automatically identified and * added to the graph. * * @param from * the first vertex of the edge * @param to * the second vertex of the edge * @param el * an edge label */ public void set(VertexLabelType from, VertexLabelType to, EdgeLabelType el) { Dictionary localEdgeLookup = checkForNewVertex(from); localEdgeLookup.Add(to, el); checkForNewVertex(to); } /** Handles new vertices. */ private Dictionary checkForNewVertex( VertexLabelType v) { Dictionary result = globalEdgeLookup[v]; if (result == null) { result = new Dictionary(); globalEdgeLookup.Add(v, result); vertexLabels.Add(v); } return result; } /** * Removes an edge from the graph. * * @param from * the first vertex of the edge * @param to * the second vertex of the edge */ public void remove(VertexLabelType from, VertexLabelType to) { Dictionary localEdgeLookup = globalEdgeLookup[from]; if (localEdgeLookup != null) localEdgeLookup.Remove(to); } /** * Returns the label of the edge between the specified vertices, or null if * there is no edge between them. * * @param from * the first vertex of the ege * @param to * the second vertex of the edge * * @return the label of the edge between the specified vertices, or null if * there is no edge between them. */ public EdgeLabelType get(VertexLabelType from, VertexLabelType to) { Dictionary localEdgeLookup = globalEdgeLookup[from]; return localEdgeLookup == null ? default(EdgeLabelType) : localEdgeLookup[to]; } /** * Returns the labels of those vertices which can be obtained by following * the edges starting at the specified vertex. */ public List getSuccessors(VertexLabelType v) { List result = new List(); Dictionary localEdgeLookup = globalEdgeLookup[v]; if (localEdgeLookup != null) result.AddRange(localEdgeLookup.Keys); return result; } /** Returns the labels of all vertices within the graph. */ public List getVertexLabels() { return vertexLabels; } /** Checks whether the given label is the label of one of the vertices. */ public bool isVertexLabel(VertexLabelType v) { return globalEdgeLookup[v] != null; } /** Removes all vertices and all edges from the graph. */ public void clear() { vertexLabels.Clear(); globalEdgeLookup.Clear(); } } } ================================================ FILE: aima-csharp/util/LinkedHashSet.cs ================================================ using System; using System.Collections; using System.Collections.Generic; namespace aima.core.util { public class LinkedHashSet : ISet { private readonly IDictionary> dict; private readonly LinkedList list; public LinkedHashSet(int initialCapacity) { this.dict = new Dictionary>(initialCapacity); this.list = new LinkedList(); } public LinkedHashSet() { this.dict = new Dictionary>(); this.list = new LinkedList(); } public LinkedHashSet(IEnumerable e) : this() { addEnumerable(e); } public LinkedHashSet(int initialCapacity, IEnumerable e) : this(initialCapacity) { addEnumerable(e); } private void addEnumerable(IEnumerable e) { foreach (T t in e) { Add(t); } } // ISet implementation public bool Add(T item) { if (this.dict.ContainsKey(item)) { return false; } LinkedListNode node = this.list.AddLast(item); this.dict[item] = node; return true; } public void ExceptWith(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } foreach (T t in other) { Remove(t); } } public void IntersectWith(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } T[] ts = new T[Count]; CopyTo(ts, 0); foreach (T t in ts) { if (!System.Linq.Enumerable.Contains(other, t)) { Remove(t); } } } public bool IsProperSubsetOf(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } int contains = 0; int noContains = 0; foreach (T t in other) { if (Contains(t)) { contains++; } else { noContains++; } } return contains == Count && noContains > 0; } public bool IsProperSupersetOf(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } int otherCount = System.Linq.Enumerable.Count(other); if (Count <= otherCount) { return false; } int contains = 0; int noContains = 0; foreach (T t in this) { if (System.Linq.Enumerable.Contains(other, t)) { contains++; } else { noContains++; } } return contains == otherCount && noContains > 0; } public bool IsSubsetOf(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } foreach (T t in this) { if (!System.Linq.Enumerable.Contains(other, t)) { return false; } } return true; } public bool IsSupersetOf(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } foreach (T t in other) { if (!Contains(t)) { return false; } } return true; } public bool Overlaps(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } foreach (T t in other) { if (Contains(t)) { return true; } } return false; } public bool SetEquals(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } int otherCount = System.Linq.Enumerable.Count(other); if (Count != otherCount) { return false; } return IsSupersetOf(other); } public void SymmetricExceptWith(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } T[] ts = new T[Count]; CopyTo(ts, 0); HashSet otherList = new HashSet(other); foreach (T t in ts) { if (otherList.Contains(t)) { Remove(t); otherList.Remove(t); } } foreach (T t in otherList) { Add(t); } } public void UnionWith(IEnumerable other) { if (other == null) { throw new ArgumentNullException("other cannot be null"); } foreach (T t in other) { Add(t); } } // ICollection implementation public int Count { get { return this.dict.Count; } } public bool IsReadOnly { get { return this.dict.IsReadOnly; } } void ICollection.Add(T item) { Add(item); } public void Clear() { this.dict.Clear(); this.list.Clear(); } public bool Contains(T item) { return this.dict.ContainsKey(item); } public void CopyTo(T[] array, int arrayIndex) { this.list.CopyTo(array, arrayIndex); } public bool Remove(T item) { LinkedListNode node; if (!this.dict.TryGetValue(item, out node)) { return false; } this.dict.Remove(item); this.list.Remove(node); return true; } // IEnumerable implementation public IEnumerator GetEnumerator() { return this.list.GetEnumerator(); } // IEnumerable implementation IEnumerator IEnumerable.GetEnumerator() { return this.list.GetEnumerator(); } } } ================================================ FILE: aima-csharp/util/Matrix.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.IO; namespace aima.core.util { /** * Jama = Java Matrix class. *

* The Java Matrix Class provides the fundamental operations of numerical linear * algebra. Various constructors create Matrices from two dimensional arrays of * double precision floating point numbers. Various "gets" and "sets" provide * access to submatrices and matrix elements. Several methods implement basic * matrix arithmetic, including matrix addition and multiplication, matrix * norms, and element-by-element array operations. Methods for reading and * printing matrices are also included. All the operations in this version of * the Matrix Class involve real matrices. Complex matrices may be handled in a * future version. *

* Five fundamental matrix decompositions, which consist of pairs or triples of * matrices, permutation vectors, and the like, produce results in five * decomposition classes. These decompositions are accessed by the Matrix class * to compute solutions of simultaneous linear equations, determinants, inverses * and other matrix functions. The five decompositions are: *

*

    *
  • Cholesky Decomposition of symmetric, positive definite matrices. *
  • LU Decomposition of rectangular matrices. *
  • QR Decomposition of rectangular matrices. *
  • Singular Value Decomposition of rectangular matrices. *
  • Eigenvalue Decomposition of both symmetric and nonsymmetric square * matrices. *
*
*
Example of use:
*

*

Solve a linear system A x = b and compute the residual norm, ||b - A x||. *

* *

     * double[][] vals = { { 1., 2., 3 }, { 4., 5., 6. }, { 7., 8., 10. } };
     * Matrix A = new Matrix(vals);
     * Matrix b = Matrix.random(3, 1);
     * Matrix x = A.solve(b);
     * Matrix r = A.times(x).minus(b);
     * double rnorm = r.normInf();
     * 
* *
*
* * @author The MathWorks, Inc. and the National Institute of Standards and * Technology. * @version 5 August 1998 */ public class Matrix { private static readonly long serialVersionUID = 1L; /* * ------------------------ Class variables ------------------------ */ /** * Array for internal storage of elements. * * @serial internal array storage. */ private readonly double[][] A; /** * Row and column dimensions. * * @serial row dimension. * @serial column dimension. */ private readonly int m, n; /* * ------------------------ Constructors ------------------------ */ /** Construct a diagonal Matrix from the given List of doubles */ public static Matrix createDiagonalMatrix(List values) { Matrix m = new Matrix(values.Count, values.Count, 0); for (int i = 0; i < values.Count; i++) { m.set(i, i, values[i]); } return m; } /** * Construct an m-by-n matrix of zeros. * * @param m * Number of rows. * @param n * Number of colums. */ public Matrix(int m, int n) { this.m = m; this.n = n; A = new double[m][]; for (int i = 0; i < m; i++) { A[i] = new double[n]; } } /** * Construct an m-by-n constant matrix. * * @param m * Number of rows. * @param n * Number of colums. * @param s * Fill the matrix with this scalar value. */ public Matrix(int m, int n, double s) { this.m = m; this.n = n; A = new double[m][]; for (int i = 0; i < m; i++) { A[i] = new double[n]; for (int j = 0; j < n; j++) { A[i][j] = s; } } } /** * Construct a matrix from a 2-D array. * * @param A * Two-dimensional array of doubles. * @exception IllegalArgumentException * All rows must have the same length * @see #constructWithCopy */ public Matrix(double[][] A) { m = A.Length; n = A[0].Length; for (int i = 0; i < m; i++) { if (A[i].Length != n) { throw new ArgumentOutOfRangeException( "All rows must have the same length."); } } this.A = A; } /** * Construct a matrix quickly without checking arguments. * * @param A * Two-dimensional array of doubles. * @param m * Number of rows. * @param n * Number of colums. */ public Matrix(double[][] A, int m, int n) { this.A = A; this.m = m; this.n = n; } /** * Construct a matrix from a one-dimensional packed array * * @param vals * One-dimensional array of doubles, packed by columns (ala * Fortran). * @param m * Number of rows. * @exception IllegalArgumentException * Array length must be a multiple of m. */ public Matrix(double[] vals, int m) { this.m = m; n = (m != 0 ? vals.Length / m : 0); if (m * n != vals.Length) { throw new ArgumentOutOfRangeException( "Array length must be a multiple of m."); } A = new double[m][]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { A[i][j] = vals[i + j * m]; } } } /* * ------------------------ Public Methods ------------------------ */ /** * Construct a matrix from a copy of a 2-D array. * * @param A * Two-dimensional array of doubles. * @exception IllegalArgumentException * All rows must have the same length */ public static Matrix constructWithCopy(double[][] A) { int m = A.Length; int n = A[0].Length; Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { if (A[i].Length != n) { throw new ArgumentOutOfRangeException( "All rows must have the same length."); } for (int j = 0; j < n; j++) { C[i][j] = A[i][j]; } } return X; } /** * Make a deep copy of a matrix */ public Matrix copy() { Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[i][j] = A[i][j]; } } return X; } /** * Clone the Matrix object. */ public Object clone() { return this.copy(); } /** * Access the internal two-dimensional array. * * @return Pointer to the two-dimensional array of matrix elements. */ public double[][] getArray() { return A; } /** * Copy the internal two-dimensional array. * * @return Two-dimensional array copy of matrix elements. */ public double[][] getArrayCopy() { double[][] C = new double[m][]; for (int i = 0; i < m; i++) { C[i] = new double[n]; for (int j = 0; j < n; j++) { C[i][j] = A[i][j]; } } return C; } /** * Make a one-dimensional column packed copy of the internal array. * * @return Matrix elements packed in a one-dimensional array by columns. */ public double[] getColumnPackedCopy() { double[] vals = new double[m * n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { vals[i + j * m] = A[i][j]; } } return vals; } /** * Make a one-dimensional row packed copy of the internal array. * * @return Matrix elements packed in a one-dimensional array by rows. */ public double[] getRowPackedCopy() { double[] vals = new double[m * n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { vals[i * n + j] = A[i][j]; } } return vals; } /** * Get row dimension. * * @return m, the number of rows. */ public int getRowDimension() { return m; } /** * Get column dimension. * * @return n, the number of columns. */ public int getColumnDimension() { return n; } /** * Get a single element. * * @param i * Row index. * @param j * Column index. * @return A(i,j) * @exception ArrayIndexOutOfBoundsException */ public double get(int i, int j) { return A[i][j]; } /** * Get a submatrix. * * @param i0 * Initial row index * @param i1 * Final row index * @param j0 * Initial column index * @param j1 * Final column index * @return A(i0:i1,j0:j1) * @exception ArrayIndexOutOfBoundsException * Submatrix indices */ public Matrix getMatrix(int i0, int i1, int j0, int j1) { Matrix X = new Matrix(i1 - i0 + 1, j1 - j0 + 1); double[][] B = X.getArray(); for (int i = i0; i <= i1; i++) { for (int j = j0; j <= j1; j++) { B[i - i0][j - j0] = A[i][j]; } } return X; } /** * Get a submatrix. * * @param r * Array of row indices. * @param c * Array of column indices. * @return A(r(:),c(:)) * @exception ArrayIndexOutOfBoundsException * Submatrix indices */ public Matrix getMatrix(int[] r, int[] c) { Matrix X = new Matrix(r.Length, c.Length); double[][] B = X.getArray(); for (int i = 0; i < r.Length; i++) { for (int j = 0; j < c.Length; j++) { B[i][j] = A[r[i]][c[j]]; } } return X; } /** * Get a submatrix. * * @param i0 * Initial row index * @param i1 * Final row index * @param c * Array of column indices. * @return A(i0:i1,c(:)) * @exception ArrayIndexOutOfBoundsException * Submatrix indices */ public Matrix getMatrix(int i0, int i1, int[] c) { Matrix X = new Matrix(i1 - i0 + 1, c.Length); double[][] B = X.getArray(); for (int i = i0; i <= i1; i++) { for (int j = 0; j < c.Length; j++) { B[i - i0][j] = A[i][c[j]]; } } return X; } /** * Get a submatrix. * * @param r * Array of row indices. * @param j0 * Initial column index * @param j1 * Final column index * @return A(r(:),j0:j1) * @exception ArrayIndexOutOfBoundsException * Submatrix indices */ public Matrix getMatrix(int[] r, int j0, int j1) { Matrix X = new Matrix(r.Length, j1 - j0 + 1); double[][] B = X.getArray(); for (int i = 0; i < r.Length; i++) { for (int j = j0; j <= j1; j++) { B[i][j - j0] = A[r[i]][j]; } } return X; } /** * Set a single element. * * @param i * Row index. * @param j * Column index. * @param s * A(i,j). * @exception ArrayIndexOutOfBoundsException */ public void set(int i, int j, double s) { A[i][j] = s; } /** * Set a submatrix. * * @param i0 * Initial row index * @param i1 * Final row index * @param j0 * Initial column index * @param j1 * Final column index * @param X * A(i0:i1,j0:j1) * @exception ArrayIndexOutOfBoundsException * Submatrix indices */ public void setMatrix(int i0, int i1, int j0, int j1, Matrix X) { for (int i = i0; i <= i1; i++) { for (int j = j0; j <= j1; j++) { A[i][j] = X.get(i - i0, j - j0); } } } /** * Set a submatrix. * * @param r * Array of row indices. * @param c * Array of column indices. * @param X * A(r(:),c(:)) * @exception ArrayIndexOutOfBoundsException * Submatrix indices */ public void setMatrix(int[] r, int[] c, Matrix X) { for (int i = 0; i < r.Length; i++) { for (int j = 0; j < c.Length; j++) { A[r[i]][c[j]] = X.get(i, j); } } } /** * Set a submatrix. * * @param r * Array of row indices. * @param j0 * Initial column index * @param j1 * Final column index * @param X * A(r(:),j0:j1) * @exception ArrayIndexOutOfBoundsException * Submatrix indices */ public void setMatrix(int[] r, int j0, int j1, Matrix X) { for (int i = 0; i < r.Length; i++) { for (int j = j0; j <= j1; j++) { A[r[i]][j] = X.get(i, j - j0); } } } /** * Set a submatrix. * * @param i0 * Initial row index * @param i1 * Final row index * @param c * Array of column indices. * @param X * A(i0:i1,c(:)) * @exception ArrayIndexOutOfBoundsException * Submatrix indices */ public void setMatrix(int i0, int i1, int[] c, Matrix X) { for (int i = i0; i <= i1; i++) { for (int j = 0; j < c.Length; j++) { A[i][c[j]] = X.get(i - i0, j); } } } /** * Matrix transpose. * * @return A' */ public Matrix transpose() { Matrix X = new Matrix(n, m); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[j][i] = A[i][j]; } } return X; } /** * One norm * * @return maximum column sum. */ public double norm1() { double f = 0; for (int j = 0; j < n; j++) { double s = 0; for (int i = 0; i < m; i++) { s += Math.Abs(A[i][j]); } f = Math.Max(f, s); } return f; } /** * Two norm * * @return maximum singular value. */ // public double norm2 () { // return (new SingularValueDecomposition(this).norm2()); // } /** * Infinity norm * * @return maximum row sum. */ public double normInf() { double f = 0; for (int i = 0; i < m; i++) { double s = 0; for (int j = 0; j < n; j++) { s += Math.Abs(A[i][j]); } f = Math.Max(f, s); } return f; } /** * Frobenius norm * * @return sqrt of sum of squares of all elements. */ // public double normF () { // double f = 0; // for (int i = 0; i < m; i++) { // for (int j = 0; j < n; j++) { // f = Maths.hypot(f,A[i][j]); // } // } // return f; // } /** * Unary minus * * @return -A */ public Matrix uminus() { Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[i][j] = -A[i][j]; } } return X; } /** * C = A + B * * @param B * another matrix * @return A + B */ public Matrix plus(Matrix B) { checkMatrixDimensions(B); Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[i][j] = A[i][j] + B.A[i][j]; } } return X; } /** * A = A + B * * @param B * another matrix * @return A + B */ public Matrix plusEquals(Matrix B) { checkMatrixDimensions(B); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { A[i][j] = A[i][j] + B.A[i][j]; } } return this; } /** * C = A - B * * @param B * another matrix * @return A - B */ public Matrix minus(Matrix B) { checkMatrixDimensions(B); Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[i][j] = A[i][j] - B.A[i][j]; } } return X; } /** * A = A - B * * @param B * another matrix * @return A - B */ public Matrix minusEquals(Matrix B) { checkMatrixDimensions(B); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { A[i][j] = A[i][j] - B.A[i][j]; } } return this; } /** * Element-by-element multiplication, C = A.*B * * @param B * another matrix * @return A.*B */ public Matrix arrayTimes(Matrix B) { checkMatrixDimensions(B); Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[i][j] = A[i][j] * B.A[i][j]; } } return X; } /** * Element-by-element multiplication in place, A = A.*B * * @param B * another matrix * @return A.*B */ public Matrix arrayTimesEquals(Matrix B) { checkMatrixDimensions(B); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { A[i][j] = A[i][j] * B.A[i][j]; } } return this; } /** * Element-by-element right division, C = A./B * * @param B * another matrix * @return A./B */ public Matrix arrayRightDivide(Matrix B) { checkMatrixDimensions(B); Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[i][j] = A[i][j] / B.A[i][j]; } } return X; } /** * Element-by-element right division in place, A = A./B * * @param B * another matrix * @return A./B */ public Matrix arrayRightDivideEquals(Matrix B) { checkMatrixDimensions(B); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { A[i][j] = A[i][j] / B.A[i][j]; } } return this; } /** * Element-by-element left division, C = A.\B * * @param B * another matrix * @return A.\B */ public Matrix arrayLeftDivide(Matrix B) { checkMatrixDimensions(B); Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[i][j] = B.A[i][j] / A[i][j]; } } return X; } /** * Element-by-element left division in place, A = A.\B * * @param B * another matrix * @return A.\B */ public Matrix arrayLeftDivideEquals(Matrix B) { checkMatrixDimensions(B); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { A[i][j] = B.A[i][j] / A[i][j]; } } return this; } /** * Multiply a matrix by a scalar, C = s*A * * @param s * scalar * @return s*A */ public Matrix times(double s) { Matrix X = new Matrix(m, n); double[][] C = X.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { C[i][j] = s * A[i][j]; } } return X; } /** * Multiply a matrix by a scalar in place, A = s*A * * @param s * scalar * @return replace A by s*A */ public Matrix timesEquals(double s) { for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { A[i][j] = s * A[i][j]; } } return this; } /** * Linear algebraic matrix multiplication, A * B * * @param B * another matrix * @return Matrix product, A * B * @exception IllegalArgumentException * Matrix inner dimensions must agree. */ public Matrix times(Matrix B) { if (B.m != n) { throw new ArgumentException( "Matrix inner dimensions must agree."); } Matrix X = new Matrix(m, B.n); double[][] C = X.getArray(); double[] Bcolj = new double[n]; for (int j = 0; j < B.n; j++) { for (int k = 0; k < n; k++) { Bcolj[k] = B.A[k][j]; } for (int i = 0; i < m; i++) { double[] Arowi = A[i]; double s = 0; for (int k = 0; k < n; k++) { s += Arowi[k] * Bcolj[k]; } C[i][j] = s; } } return X; } /** * LU Decomposition * * @return LUDecomposition * @see LUDecomposition */ public LUDecomposition lu() { return new LUDecomposition(this); } // /** QR Decomposition // @return QRDecomposition // @see QRDecomposition // */ // // public QRDecomposition qr () { // return new QRDecomposition(this); // } // // /** Cholesky Decomposition // @return CholeskyDecomposition // @see CholeskyDecomposition // */ // // public CholeskyDecomposition chol () { // return new CholeskyDecomposition(this); // } // // /** Singular Value Decomposition // @return SingularValueDecomposition // @see SingularValueDecomposition // */ // // public SingularValueDecomposition svd () { // return new SingularValueDecomposition(this); // } // // /** Eigenvalue Decomposition // @return EigenvalueDecomposition // @see EigenvalueDecomposition // */ // // public EigenvalueDecomposition eig () { // return new EigenvalueDecomposition(this); // } /** * Solve A*X = B * * @param B * right hand side * @return solution if A is square, least squares solution otherwise */ public Matrix solve(Matrix B) { // assumed m == n return new LUDecomposition(this).solve(B); } /** * Solve X*A = B, which is also A'*X' = B' * * @param B * right hand side * @return solution if A is square, least squares solution otherwise. */ public Matrix solveTranspose(Matrix B) { return transpose().solve(B.transpose()); } /** * Matrix inverse or pseudoinverse * * @return inverse(A) if A is square, pseudoinverse otherwise. */ public Matrix inverse() { return solve(identity(m, m)); } /** * Matrix determinant * * @return determinant */ public double det() { return new LUDecomposition(this).det(); } /** * Matrix rank * * @return effective numerical rank, obtained from SVD. */ // public int rank () { // return new SingularValueDecomposition(this).rank(); // } // // /** Matrix condition (2 norm) // @return ratio of largest to smallest singular value. // */ // // public double cond () { // return new SingularValueDecomposition(this).cond(); // } /** * Matrix trace. * * @return sum of the diagonal elements. */ public double trace() { double t = 0; for (int i = 0; i < Math.Min(m, n); i++) { t += A[i][i]; } return t; } /** * Generate matrix with random elements * * @param m * Number of rows. * @param n * Number of colums. * @return An m-by-n matrix with uniformly distributed random elements. */ public static Matrix random(int m, int n) { Matrix A = new Matrix(m, n); Random r = new Random(); double[][] X = A.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { X[i][j] = r.NextDouble(); } } return A; } /** * Generate identity matrix * * @param m * Number of rows. * @param n * Number of colums. * @return An m-by-n matrix with ones on the diagonal and zeros elsewhere. */ public static Matrix identity(int m, int n) { Matrix A = new Matrix(m, n); double[][] X = A.getArray(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { X[i][j] = (i == j ? 1.0 : 0.0); } } return A; } public String toString() { StringBuilder buf = new StringBuilder(); for (int i = 0; i < getRowDimension(); i++) { for (int j = 0; j < getColumnDimension(); j++) { buf.Append(get(i, j)); buf.Append(" "); } buf.Append("\n"); } return buf.ToString(); } /* * ------------------------ Private Methods ------------------------ */ /** Check if size(A) == size(B) * */ private void checkMatrixDimensions(Matrix B) { if (B.m != m || B.n != n) { throw new ArgumentException("Matrix dimensions must agree."); } } } } ================================================ FILE: aima-csharp/util/MixedRadixNumber.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * For details on Mixed Radix Number Representations. * * @author Ciaran O'Reilly * @author Mike Stampone */ public class MixedRadixNumber { // private static readonly long serialVersionUID = 1L; // private long value = 0L; private long maxValue = 0L; private int[] radices = null; private int[] currentNumeralValue = null; private bool recalculate = true; /** * Constructs a mixed radix number with a specified value and a specified * array of radices. * * @param value * the value of the mixed radix number * @param radices * the radices used to represent the value of the mixed radix * number */ public MixedRadixNumber(long value, int[] radices) { this.value = value; this.radices = new int[radices.Length]; Array.Copy(radices, 0, this.radices, 0, radices.Length); calculateMaxValue(); } /** * Constructs a mixed radix number with a specified value and a specified * list of radices. * * @param value * the value of the mixed radix number * @param radices * the radices used to represent the value of the mixed radix * number */ public MixedRadixNumber(long value, List radices) { this.value = value; this.radices = new int[radices.Capacity]; for(int i = 0; i < radices.Capacity; i++) { this.radices[i] = radices[i]; } calculateMaxValue(); } /** * Constructs a mixed radix number with a specified array of numerals and a * specified array of radices. * * @param radixValues * the numerals of the mixed radix number * @param radices * the radices of the mixed radix number */ public MixedRadixNumber(int[] radixValues, int[] radices): this(0, radices) { setCurrentValueFor(radixValues); } /** * Returns the value of the mixed radix number with the specified array of * numerals and the current array of radices. * * @return the value of the mixed radix number * * @throws IllegalArgumentException * if any of the specified numerals is less than zero, or if any * of the specified numerals is greater than it's corresponding * radix. */ public long getCurrentValueFor(int[] radixValues) { if(radixValues.Length != radices.Length) { throw new ArgumentException("Radix values not same size as Radices"); } long cvalue = 0; long mvalue = 1; for (int i = 0; i < radixValues.Length; i++) { if (radixValues[i] < 0 || radixValues[i] >= radices[i]) { throw new ArgumentException("Radix value " + i + " is out of range for radix at this position"); } if (i > 0) { mvalue *= radices[i - 1]; } cvalue += mvalue * radixValues[i]; } return cvalue; } /** * Sets the value of the mixed radix number with the specified array of * numerals and the current array of radices. * * @param radixValues * the numerals of the mixed radix number */ public void setCurrentValueFor(int[] radixValues) { this.value = getCurrentValueFor(radixValues); Array.Copy(radixValues, 0, this.currentNumeralValue, 0, radixValues.Length); recalculate = false; } /** * Returns the maximum value which can be represented by the current array * of radices. * * @return the maximum value which can be represented by the current array * of radices. */ public long getMaxAllowedValue() { return maxValue; } /** * Increments the value of the mixed radix number, if the value is less than * the maximum value which can be represented by the current array of * radices. * * @return true if the increment was successful. */ public bool increment() { if (value < maxValue) { value++; recalculate = true; return true; } return false; } /** * Decrements the value of the mixed radix number, if the value is greater * than zero. * * @return true if the decrement was successful. */ public bool decrement() { if (value > 0) { value--; recalculate = true; return true; } return false; } /** * Returns the numeral at the specified position. * * @param atPosition * the position of the numeral to return * @return the numeral at the specified position. */ public int getCurrentNumeralValue(int atPosition) { if (atPosition >= 0 && atPosition < radices.Length) { if (recalculate) { long quotient = value; for (int i = 0; i < radices.Length; i++) { if (0 != quotient) { currentNumeralValue[i] = (int)quotient % radices[i]; quotient = quotient / radices[i]; } else { currentNumeralValue[i] = 0; } } recalculate = false; } return currentNumeralValue[atPosition]; } throw new ArgumentException( "Argument atPosition must be >=0 and < " + radices.Length); } // START_Number public int intValue() { return (int)longValue(); } public long longValue() { return value; } public float floatValue() { return longValue(); } public double doubleValue() { return longValue(); } // END-Number public String toString() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); for (int i = 0; i < radices.Length; i++) { sb.Append("["); sb.Append(this.getCurrentNumeralValue(i)); sb.Append("]"); } return sb.ToString(); } // PRIVATE /** * Sets the maximum value which can be represented by the current array of * radices. * * @throws IllegalArgumentException * if no radices are defined, if any radix is less than two, or * if the current value is greater than the maximum value which * can be represented by the current array of radices. */ private void calculateMaxValue() { if (0 == radices.Length) { throw new ArgumentException( "At least 1 radix must be defined."); } for (int i = 0; i < radices.Length; i++) { if (radices[i] < 2) { throw new ArgumentException( "Invalid radix, must be >= 2"); } } // Calculate the maxValue allowed maxValue = radices[0]; for (int i = 1; i < radices.Length; i++) { maxValue *= radices[i]; } maxValue -= 1; if (value > maxValue) { throw new ArgumentException( "The value [" + value + "] cannot be represented with the radices provided, max value is " + maxValue); } currentNumeralValue = new int[radices.Length]; } } } ================================================ FILE: aima-csharp/util/MockRandomizer.cs ================================================ using System; namespace aima.core.util { /** * Mock implementation of the Randomizer interface so that the set of Random * numbers returned are in fact predefined. * * @author Ravi Mohan * */ public class MockRandomizer : Randomizer { private double[] values; private int index; /** * * @param values * the set of predetermined random values to loop over. */ public MockRandomizer(double[] values) { this.values = new double[values.Length]; System.Array.Copy(values, 0, this.values, 0, values.Length); this.index = 0; } // START-Randomizer public double nextDouble() { if(index == values.Length) { index = 0; } return values[index++]; } //END-Randomizer } } ================================================ FILE: aima-csharp/util/Pair.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * @author Ravi Mohan * @author Mike Stampone * */ public class Pair { private X a; private Y b; /** * Constructs a Pair from two given elements * * @param a * the first element * @param b * the second element */ public Pair(X a, Y b) { this.a = a; this.b = b; } /** * Returns the first element of the pair * * @return the first element of the pair */ public X getFirst() { return a; } /** * Returns the second element of the pair * * @return the second element of the pair */ public Y getSecond() { return b; } public override bool Equals(Object o) { if (o is Pair) { Pair p = (Pair)o; return a.Equals(p.a) && b.Equals(p.b); } return false; } public int hashCode() { return a.GetHashCode() + 31 * b.GetHashCode(); } public override String ToString() { return "< " + getFirst().ToString() + " , " + getSecond().ToString() + " > "; } } } ================================================ FILE: aima-csharp/util/Point2D.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * Simplified version of java.awt.geom.Point2D. We do not want * dependencies to presentation layer packages here. * * @author R. Lunde * @author Mike Stampone */ public class Point2D { private double x; private double y; public Point2D(double x, double y) { this.x = x; this.y = y; } /** * Returns the X coordinate of this Point2D in * double precision. * * @return the X coordinate of this Point2D. */ public double getX() { return x; } /** * Returns the Y coordinate of this Point2D in * double precision. * * @return the Y coordinate of this Point2D. */ public double getY() { return y; } /** * Returns the Euclidean distance between a specified point and this point. * * @return the Euclidean distance between a specified point and this point. */ public double distance(Point2D pt) { // Distance Between X Coordinates double x_distance = (pt.getX() - x) * (pt.getX() - x); // Distance Between Y Coordinates double y_distance = (pt.getY() - y) * (pt.getY() - y); // Distance Between 2d Points double total_distance = Math.Sqrt(x_distance + y_distance); return total_distance; } } } ================================================ FILE: aima-csharp/util/Randomizer.cs ================================================ namespace aima.core.util { /** * @author Ravi Mohan * */ public interface Randomizer { /** * * @return a double value, chosen (approximately) uniformly from the range * [0.0d, 1.0d), i.e. 0.0d (inclusive) to 1.0d (exclusive). */ double nextDouble(); } } ================================================ FILE: aima-csharp/util/SetOps.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace aima.core.util { /** * Note: This code is based on - Java Tutorial: The Set Interface
* * Using LinkedHashSet, even though slightly slower than HashSet, in order to * ensure order is always respected (i.e. if called with TreeSet or * LinkedHashSet implementations). * * @author Ciaran O'Reilly * @author Ravi Mohan */ public class SetOps { /** * * @param * @param s1 * @param s2 * @return the union of s1 and s2. (The union of two sets is the set * containing all of the elements contained in either set.) */ public static List union(List s1, List s2) { if(s1 == s2) { return s1; } LinkedHashSet union = new LinkedHashSet(s1); union.UnionWith(s2); return union.ToList(); } /** * * @param * @param s1 * @param s2 * @return the intersection of s1 and s2. (The intersection of two sets is * the set containing only the elements common to both sets.) */ public static List intersection(List s1, List s2) { if (s1 == s2) { return s1; } LinkedHashSet intersection = new LinkedHashSet(s1); intersection.IntersectWith(s2); return intersection.ToList(); } /** * * @param * @param s1 * @param s2 * @return the (asymmetric) set difference of s1 and s2. (For example, the * set difference of s1 minus s2 is the set containing all of the * elements found in s1 but not in s2.) */ public static List difference(List s1, List s2) { LinkedHashSet difference = new LinkedHashSet(s1); difference.ExceptWith(s2); return difference.ToList(); } } } ================================================ FILE: aima-csharp/util/Table.cs ================================================ using System.Collections.Generic; using System.Text; namespace aima.core.util { /** * @author Ravi Mohan * @author Mike Stampone * */ public class Table // where ValueType : struct { private List rowHeaders; private List columnHeaders; private Dictionary> rows; public Table(List rowHeaders, List columnHeaders) { this.rowHeaders = rowHeaders; this.columnHeaders = columnHeaders; this.rows = new Dictionary>(); foreach (RowHeaderType rowHeader in rowHeaders) { rows.Add(rowHeader, new Dictionary()); } } public void set(RowHeaderType r, ColumnHeaderType c, ValueType v) { if (rows[r].ContainsKey(c)) { rows[r][c] = v; } else { rows[r].Add(c, v); } } public ValueType get(RowHeaderType r, ColumnHeaderType c) { if (!rows.ContainsKey(r)) { return default(ValueType); } Dictionary rowValues = rows[r]; if (rowValues == null || !rowValues.ContainsKey(c)) { return default(ValueType); } return rowValues[c]; } public override System.String ToString() { StringBuilder buf = new StringBuilder(); foreach (RowHeaderType r in rowHeaders) { foreach (ColumnHeaderType c in columnHeaders) { buf.Append(get(r, c).ToString()); buf.Append(" "); } buf.Append("\n"); } return buf.ToString(); } class Row { private Dictionary c; public Row() { this.c = new Dictionary(); } public Dictionary cells() { return this.c; } } class Cell { private ValueHeaderType val; public Cell() { val = default(ValueHeaderType); } public Cell(ValueHeaderType value) { this.val = value; } public void set(ValueHeaderType value) { this.val = value; } public ValueHeaderType value() { return val; } } } } ================================================ FILE: aima-csharp/util/Triplet.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * @author Ravi Mohan * @author Mike Stampone * */ public class Triplet { private X x; private Y y; private Z z; /** * Constructs a triplet with three specified elements. * * @param x * the first element of the triplet. * @param y * the second element of the triplet. * @param z * the third element of the triplet. */ public Triplet(X x, Y y, Z z) { this.x = x; this.y = y; this.z = z; } /** * Returns the second element of the triplet. * * @return the second element of the triplet. */ public Y getSecond() { return y; } /** * Returns the third element of the triplet. * * @return the third element of the triplet. */ public Z getThird() { return z; } public override bool Equals(Object o) { if (o is Triplet) { Triplet other = (Triplet)o; return (x.Equals(other.x)) && (y.Equals(other.y)) && (y.Equals(other.y)); } return false; } public int hashCode() { return x.GetHashCode() + 31 * y.GetHashCode() + 31 * z.GetHashCode(); } public String toString() { return "< " + x.ToString() + " , " + y.ToString() + " , " + z.ToString() + " >"; } } } ================================================ FILE: aima-csharp/util/TwoKeyHashMap.cs ================================================ using System.Collections.Generic; namespace aima.core.util { /** * Provides a hash map which is indexed by two keys. In fact this is just a hash * map which is indexed by a pair containing the two keys. The provided two-key * access methods try to increase code readability. * * @param * First key * @param * Second key * @param * Result value * * @author Ruediger Lunde * @author Mike Stampone */ public class TwoKeyHashMap : Dictionary, V> { private static readonly long serialVersionUID = -2232849394229644162L; /** * Associates the specified value with the specified key pair in this map. * If the map previously contained a mapping for this key pair, the old * value is replaced. * * @param key1 * the first key of the key pair, with which the specified value * is to be associated. * @param key2 * the second key of the key pair, with which the specified value * is to be associated. * @param value * the value to be associated with the key pair. * */ public void put(K1 key1, K2 key2, V value) { base.Add(new Pair(key1, key2), value); } /** * Returns the value to which the specified key pair is mapped in this two * key hash map, or null if the map contains no mapping for * this key pair. A return value of null does not necessarily * indicate that the map contains no mapping for the key; it is also * possible that the map explicitly maps the key to null. The * containsKey method may be used to distinguish these two * cases. * * @param key1 * the first key of the key pair, whose associated value is to be * returned. * @param key2 * the second key of the key pair, whose associated value is to * be returned. * * @return the value to which this map maps the specified key pair, or * null if the map contains no mapping for this key * pair. */ public V get(K1 key1, K2 key2) { V value; if(base.TryGetValue(new Pair(key1, key2), out value)) { return value; } return value; } /** * Returns true if this map contains a mapping for the * specified key pair. * * @param key1 * the first key of the key pair whose presence in this map is to * be tested. * @param key2 * the second key of the key pair whose presence in this map is * to be tested. * * @return true if this map contains a mapping for the * specified key. */ public bool containsKey(K1 key1, K2 key2) { return base.ContainsKey(new Pair(key1, key2)); } /** * Removes the mapping for this key pair from this map if present. * * @param key1 * the first key of the key pair, whose mapping is to be removed * from the map. * @param key2 * the second key of the key pair, whose mapping is to be removed * from the map. * * @return the previous value associated with the specified key pair, or * null if there was no mapping for the key pair. A * null return can also indicate that the map * previously associated null with the specified key * pair. */ public V removeKey(K1 key1, K2 key2, V value) { base.Remove(new Pair(key1, key2)); return value; } // public static void main(String[] args) { // TwoKeyHashMap hash = new TwoKeyHashMap(); // hash.put("A", "A", "C"); // hash.put("A", "A", "D"); // hash.put("B", "A", "E"); // System.Console.WriteLine(hash.get("A", "A")); // System.Console.WriteLine(hash.get("A", "B")); // } } } ================================================ FILE: aima-csharp/util/Util.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace aima.core.util { /** * @author Ravi Mohan * */ public class Util { public const String NO = "No"; public const String YES = "Yes"; private static Random r = new Random(); /** * Get the first element from a list. * * @param l * the list the first element is to be extracted from. * @return the first element of the passed in list. */ public static T first(List l) { return l[0]; } /** * Get a sublist of all of the elements in the list except for first. * * @param l * the list the rest of the elements are to be extracted from. * @return a list of all of the elements in the passed in list except for * the first element. */ public static List rest(List l) { List newList = l.GetRange(1, l.Count - 1); return newList; } /** * Create a set for the provided values. * @param values * the sets initial values. * @return a Set of the provided values. */ public static HashSet createSet(params T[] values) { HashSet set = new HashSet(); foreach(T t in values) { set.Add(t); } return set; } public static bool randombool() { int trueOrFalse = r.Next(2); return (!(trueOrFalse == 0)); } /** * Randomly select an element from a list. * * @param * the type of element to be returned from the list l. * @param l * a list of type T from which an element is to be selected * randomly. * @return a randomly selected element from l. */ public static T selectRandomlyFromList(List l) { return l[r.Next(l.Count)]; } public static double[] normalize(double[] probDist) { int len = probDist.Length; double total = 0.0; foreach (double d in probDist) { total = total + d; } double[] normalized = new double[len]; if (total != 0) { for (int i = 0; i < len; i++) { normalized[i] = probDist[i] / total; } } return normalized; } public static List normalize(List values) { double[] valuesAsArray = new double[values.Count]; for (int i = 0; i < valuesAsArray.Length; i++) { valuesAsArray[i] = values[i]; } double[] normalized = normalize(valuesAsArray); List results = new List(); for (int i = 0; i < normalized.Length; i++) { results.Add(normalized[i]); } return results; } public static int min(int i, int j) { return (i > j ? j : i); } public static int max(int i, int j) { return (i < j ? j : i); } public static int max(int i, int j, int k) { return max(max(i, j), k); } public static int min(int i, int j, int k) { return min(min(i, j), k); } public static T mode(List l) { Dictionary hash = new Dictionary(); foreach (T obj in l) { if (hash.ContainsKey(obj)) { hash[obj] = hash[obj] + 1; } else { hash.Add(obj, 1); } } T maxkey = hash.Keys.FirstOrDefault(); foreach (T key in hash.Keys) { if (hash[key] > hash[maxkey]) { maxkey = key; } } return maxkey; } public static String[] yesno() { return new String[] { YES, NO }; } public static double log2(double d) { return System.Math.Log(d) / System.Math.Log(2); } public static double information(double[] probabilities) { double total = 0.0; foreach (double d in probabilities) { total += (-1.0 * log2(d) * d); } return total; } public static List removeFrom(List list, T member) { List newList = new List(list); newList.Remove(member); return newList; } public static double sumOfSquares(List list) { double accum = 0; foreach (T item in list) { accum = accum + (Convert.ToDouble(item) * Convert.ToDouble(item)); } return accum; } public static String ntimes(String s, int n) { StringBuilder buf = new StringBuilder(); for (int i = 0; i < n; i++) { buf.Append(s); } return buf.ToString(); } public static void checkForNanOrInfinity(double d) { if (Double.IsNaN(d)) { throw new ArgumentException("Not a Number"); } if (Double.IsInfinity(d)) { throw new ArgumentException("Infinite Number"); } } public static int randomNumberBetween(int i, int j) { /* i,j bothinclusive */ return r.Next(j - i + 1) + i; } public static double calculateMean(List lst) { Double sum = 0.0; foreach (Double d in lst) { sum = sum + d; } return sum / lst.Count; } public static double calculateStDev(List values, double mean) { int listSize = values.Count; Double sumOfDiffSquared = 0.0; foreach (Double value in values) { double diffFromMean = value - mean; sumOfDiffSquared += ((diffFromMean * diffFromMean) / (listSize - 1)); // division moved here to avoid sum becoming too big if this // doesn't work use incremental formulation } double variance = sumOfDiffSquared; // (listSize - 1); // assumes at least 2 members in list. return System.Math.Sqrt(variance); } public static List normalizeFromMeanAndStdev(List values, double mean, double stdev) { List normalized = new List(); foreach (Double d in values) { normalized.Add((d - mean) / stdev); } return normalized; } public static double generateRandomDoubleBetween(double lowerLimit, double upperLimit) { return lowerLimit + ((upperLimit - lowerLimit) * r.NextDouble()); } } } ================================================ FILE: aima-csharp/util/Vector.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * @author Ravi Mohan * @author Mike Stampone */ public class Vector : Matrix { private static readonly long serialVersionUID = 1L; // Vector is modelled as a matrix with a single column; /** * Constructs a vector with the specified size. * * @param size * the capacity of the vector */ public Vector(int size) : base(size, 1) { } /** * Constructs a vector with the specified list of values. * * @param lst * a list of values */ public Vector(List lst) : base(lst.Count, 1) { for (int i = 0; i < lst.Count; i++) { setValue(i, lst[i]); } } /** * Returns the value at the specified index. * * @param i * the index of the value to return. * * @return the value at the specified index. */ public double getValue(int i) { return base.get(i, 0); } /** * Sets the value at the specified index. * * @param index * the index of the value to set. * @param value * the value to be placed at the index. */ public void setValue(int index, double value) { base.set(index, 0, value); } /** * Returns a copy of this vector. * * @return a copy of this vector. */ public Vector copyVector() { Vector result = new Vector(getRowDimension()); for (int i = 0; i < getRowDimension(); i++) { result.setValue(i, getValue(i)); } return result; } /** * Returns the number of values in this vector. * * @return the number of values in this vector. */ public int size() { return getRowDimension(); } /** * Returns the result of vector subtraction. * * @param v * the vector to subtract * * @return the result of vector subtraction. */ public Vector minus(Vector v) { Vector result = new Vector(size()); for (int i = 0; i < size(); i++) { result.setValue(i, getValue(i) - v.getValue(i)); } return result; } /** * Returns the result of vector addition. * * @param v * the vector to add * * @return the result of vector addition. */ public Vector plus(Vector v) { Vector result = new Vector(size()); for (int i = 0; i < size(); i++) { result.setValue(i, getValue(i) + v.getValue(i)); } return result; } /** * Returns the index at which the maximum value in this vector is located. * * @return the index at which the maximum value in this vector is located. * * @throws RuntimeException * if the vector does not contain any values. */ public int indexHavingMaxValue() { if (size() <= 0) { throw new InvalidOperationException("can't perform this op on empty vector"); } int res = 0; for (int i = 0; i < size(); i++) { if (getValue(i) > getValue(res)) { res = i; } } return res; } } } ================================================ FILE: aima-csharp/util/XYLocation.cs ================================================ using System; using System.Collections.Generic; namespace aima.core.util { /** * Note: If looking at a rectangle - the coordinate (x=0, y=0) will be the top * left hand corner. This corresponds with Java's AWT coordinate system. * * @author Ravi Mohan * @author Mike Stampone */ public class XYLocation { public enum Direction { North, South, East, West }; int xCoOrdinate, yCoOrdinate; /** * Constructs and initializes a location at the specified (x, * y) location in the coordinate space. * * @param x * the x coordinate * @param y * the y coordinate */ public XYLocation(int x, int y) { xCoOrdinate = x; yCoOrdinate = y; } /** * Returns the X coordinate of the location in integer precision. * * @return the X coordinate of the location in double precision. */ public int getXCoOrdinate() { return xCoOrdinate; } public int getYCoOrdinate() { return yCoOrdinate; } /** * Returns the location one unit left of this location. * * @return the location one unit left of this location. */ public XYLocation west() { return new XYLocation(xCoOrdinate - 1, yCoOrdinate); } /** * Returns the location one unit right of this location. * * @return the location one unit right of this location. */ public XYLocation east() { return new XYLocation(xCoOrdinate + 1, yCoOrdinate); } /** * Returns the location one unit ahead of this location. * * @return the location one unit ahead of this location. */ public XYLocation north() { return new XYLocation(xCoOrdinate, yCoOrdinate - 1); } /** * Returns the location one unit behind, this location. * * @return the location one unit behind this location. */ public XYLocation south() { return new XYLocation(xCoOrdinate, yCoOrdinate + 1); } /** * Returns the location one unit left of this location. * * @return the location one unit left of this location. */ public XYLocation left() { return west(); } /** * Returns the location one unit right of this location. * * @return the location one unit right of this location. */ public XYLocation right() { return east(); } /** * Returns the location one unit above this location. * * @return the location one unit above this location. */ public XYLocation up() { return north(); } /** * Returns the location one unit below this location. * * @return the location one unit below this location. */ public XYLocation down() { return south(); } /** * Returns the location one unit from this location in the specified * direction. * * @return the location one unit from this location in the specified * direction. */ public XYLocation locationAt(Direction direction) { if (direction.Equals(Direction.North)) { return north(); } if (direction.Equals(Direction.South)) { return south(); } if (direction.Equals(Direction.East)) { return east(); } if (direction.Equals(Direction.West)) { return west(); } else { throw new SystemException("Unknown direction " + direction); } } public bool equals(Object o) { if (null == o || !(o is XYLocation)) { return base.Equals(o); } XYLocation anotherLoc = (XYLocation)o; return ((anotherLoc.getXCoOrdinate() == xCoOrdinate) && (anotherLoc .getYCoOrdinate() == yCoOrdinate)); } public String toString() { return " ( " + xCoOrdinate + " , " + yCoOrdinate + " ) "; } public int hashCode() { int result = 17; result = 37 * result + xCoOrdinate; result = 43 * result + yCoOrdinate; return result; } } } ================================================ FILE: aima-csharp-unity/.gitignore ================================================ /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ /[Bb]uild/ /[Bb]uilds/ /Assets/AssetStoreTools* # Autogenerated VS/MD solution and project files ExportedObj/ *.csproj *.unityproj *.sln *.suo *.tmp *.user *.userprefs *.pidb *.booproj *.svd # Unity3D generated meta files *.pidb.meta # Unity3D Generated File On Crash Reports sysinfo.txt # Builds *.apk *.unitypackage ================================================ FILE: aima-csharp-unity/README.md ================================================ # ![](https://github.com/aimacode/aima-java/blob/gh-pages/aima3e/images/aima3e.jpg)aima-csharp-unity ![Unity](https://madewith.unity.com/profiles/mwu/themes/custom/mwu_theme/logo.png) Unity implementation of algorithms from [Russell](http://www.cs.berkeley.edu/~russell/) And [Norvig's](http://www.norvig.com/) [Artificial Intelligence - A Modern Approach 3rd Edition](http://aima.cs.berkeley.edu/). You can use this in conjunction with a course on AI, or for study on your own. ================================================ FILE: aima-csharp.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aima-csharp", "aima-csharp\aima-csharp.csproj", "{125F53D5-1CCF-4DAF-82FE-4324686CF417}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {125F53D5-1CCF-4DAF-82FE-4324686CF417}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {125F53D5-1CCF-4DAF-82FE-4324686CF417}.Debug|Any CPU.Build.0 = Debug|Any CPU {125F53D5-1CCF-4DAF-82FE-4324686CF417}.Release|Any CPU.ActiveCfg = Release|Any CPU {125F53D5-1CCF-4DAF-82FE-4324686CF417}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal