From 92b0477e878a0b3f082c17122957c590c31258a0 Mon Sep 17 00:00:00 2001 From: ethiril Date: Wed, 7 Oct 2026 19:44:30 +0100 Subject: [PATCH 1/2] M2.5 Part 6 --- .github/tickets/README.md | 7 +- .../tickets/m2.5/m2.5-p6-1-test-fixtures.md | 2 +- .../tickets/m2.5/m2.5-p6-2-project-split.md | 2 +- .github/tickets/m2/m2-p3-1-streaming-tasks.md | 2 +- .../tickets/m2/m2-p3-2-timing-robustness.md | 2 +- .github/tickets/m3/m3-p1-1-control-parts.md | 2 +- .../tickets/m3/m3-p1-2-function-programs.md | 2 +- .../m3/m3-p1-3-compile-programs-to-parts.md | 2 +- .../m3/m3-p1-4-compiled-parts-benchmark.md | 2 +- .github/tickets/m3/m3-p2-1-part-programs.md | 2 +- .github/tickets/m3/m3-p2-2-lower-programs.md | 2 +- .../tickets/m3/m3-p2-3-part-program-search.md | 2 +- .../tickets/m3/m3-p2-5-arithmetic-depth.md | 2 +- .../m3-p3-1-counterexample-guided-search.md | 2 +- .../m3/m3-p3-2-proof-by-composition.md | 2 +- .../tickets/m3/m3-p4-1-bit-serial-adder.md | 2 +- .github/tickets/m3/m3-p4-2-part-families.md | 2 +- .../tickets/m3/m3-p4-3-library-compression.md | 2 +- .github/tickets/m4/m4-p1-1-algorithm-spec.md | 2 +- .github/tickets/m4/m4-p1-2-recurrence-task.md | 2 +- .github/tickets/m4/m4-p1-3-relation-specs.md | 2 +- .github/tickets/m4/m4-p1-4-stream-specs.md | 2 +- .../tickets/m4/m4-p2-1-verification-steps.md | 2 +- .github/tickets/m4/m4-p2-2-superopt.md | 2 +- .github/tickets/m4/m4-p2-3-synthesis-arm.md | 2 +- .github/tickets/m4/m4-p3-1-spec-files.md | 2 +- .github/tickets/m4/m4-p3-2-result-bundle.md | 2 +- .../tickets/m4/m4-p4-1-arithmetic-records.md | 2 +- .../tickets/m4/m4-p4-2-recurrence-family.md | 2 +- .../tickets/m4/m4-p4-3-inductive-proofs.md | 2 +- .github/tickets/m5/m5-p1-1-synapse-weights.md | 2 +- .github/tickets/m5/m5-p1-2-targets.md | 2 +- .github/tickets/m5/m5-p1-3-nir-conformance.md | 2 +- .github/tickets/m5/m5-p2-1-smt-encoding.md | 2 +- .../m5/m5-p2-3-windowed-improvement.md | 2 +- .github/tickets/m5/m5-p2-4-proven-rewrites.md | 4 +- .../m5/m5-p2-5-enumerative-baseline.md | 2 +- .../tickets/m5/m5-p2-6-search-benchmark.md | 2 +- .../tickets/m5/m5-p3-1-timing-primitives.md | 2 +- .../tickets/m5/m5-p3-2-binary-primitives.md | 2 +- .github/tickets/m5/m5-p3-3-state-machines.md | 2 +- .github/tickets/m5/m5-p3-4-library-release.md | 2 +- .github/tickets/where.py | 71 ++++ README.md | 61 +-- SNP_Evolution/Application/Application.csproj | 8 + .../Application/BenchmarkService.cs | 12 +- .../Application/Catalog.cs | 10 +- .../Application/CompileService.cs | 18 +- .../Application/ComposeService.cs | 6 +- .../Application/EngineChoice.cs | 2 +- .../Application/EvolutionSession.cs | 28 +- .../Application/EvolveService.cs | 24 +- .../Application/ExportService.cs | 16 +- .../{SNP_Evolution => }/Application/Loaded.cs | 2 +- .../Application/NetworkRunService.cs | 14 +- .../Application/OutputTarget.cs | 2 +- .../Application/PartLibraries.cs | 8 +- .../Application/PartsService.cs | 16 +- .../Application/ReachService.cs | 14 +- .../Application/RunAdvisor.cs | 14 +- .../Application/RunFolders.cs | 2 +- .../Application/RunLayers.cs | 12 +- .../Application/RunNotes.cs | 8 +- .../Application/RunOutput.cs | 16 +- .../Application/RunSeed.cs | 2 +- .../Application/Settings.cs | 20 +- .../Application/TargetPatterns.cs | 0 .../Application/VerifyService.cs | 10 +- .../{SNP_Evolution => }/Cli/BenchmarkMenu.cs | 8 +- SNP_Evolution/Cli/Cli.csproj | 16 + .../{SNP_Evolution => }/Cli/CommandLine.cs | 0 .../Cli/Commands/AdviseCommand.cs | 0 .../Cli/Commands/BenchmarkArgs.cs | 8 +- .../Cli/Commands/Command.cs | 0 .../Cli/Commands/CommandArgs.cs | 0 .../Cli/Commands/CommandRegistry.cs | 0 .../Cli/Commands/CommonOptions.cs | 0 .../Cli/Commands/CompileCommand.cs | 0 .../Cli/Commands/ComposeCommand.cs | 2 +- .../Cli/Commands/EvolveCommand.cs | 2 +- .../Cli/Commands/EvolvePartsCommand.cs | 2 +- .../Cli/Commands/ExportCommands.cs | 0 .../Cli/Commands/ListCommands.cs | 4 +- .../Cli/Commands/Option.cs | 0 .../Cli/Commands/ReachCommand.cs | 0 .../Cli/Commands/TargetArgs.cs | 0 .../Cli/Commands/ValueKind.cs | 0 .../Cli/Commands/VerifyCommand.cs | 2 +- .../{SNP_Evolution => }/Cli/CompileMenu.cs | 0 .../{SNP_Evolution => }/Cli/ConsoleInput.cs | 0 .../{SNP_Evolution => }/Cli/ConsoleUi.cs | 0 .../{SNP_Evolution => }/Cli/EvolveMenu.cs | 2 +- .../{SNP_Evolution => }/Cli/InputParsing.cs | 0 .../{SNP_Evolution => }/Cli/MainMenu.cs | 0 .../{SNP_Evolution => }/Cli/MenuPrompts.cs | 0 .../{SNP_Evolution => }/Cli/PartsMenu.cs | 2 +- .../{SNP_Evolution => Cli}/Program.cs | 2 +- .../{SNP_Evolution => }/Cli/RedoMenu.cs | 0 .../{SNP_Evolution => }/Cli/RunMenu.cs | 4 +- .../{SNP_Evolution => }/Cli/SavedRuns.cs | 6 +- .../{SNP_Evolution => }/Cli/SearchMenu.cs | 0 .../{SNP_Evolution => }/Cli/SettingOptions.cs | 2 +- .../{SNP_Evolution => }/Cli/SettingsMenu.cs | 2 +- .../{SNP_Evolution => }/Cli/SettingsPage.cs | 0 .../{SNP_Evolution => }/Cli/TargetMenu.cs | 0 SNP_Evolution/Compilation/Compilation.csproj | 7 + .../Compilation/Recurrence.cs | 0 .../Compilation/RecurrenceCompiler.cs | 2 +- .../Compilation/RegisterMachine.cs | 0 .../Compilation/RegisterMachineCompiler.cs | 2 +- SNP_Evolution/Directory.Build.props | 8 +- SNP_Evolution/Export/Export.csproj | 7 + .../Export/ExternalTool.cs | 0 .../{SNP_Evolution => }/Export/NetworkPort.cs | 6 +- .../{SNP_Evolution => }/Export/NirExporter.cs | 8 +- .../Export/UppaalDeclarations.cs | 10 +- .../Export/UppaalExporter.cs | 10 +- .../Export/VerilogExporter.cs | 8 +- .../Export/VerilogNeuronModules.cs | 2 +- .../Export/VerilogTestbench.cs | 4 +- .../Networks => Model}/DelayKind.cs | 2 +- .../Networks => Model}/ExpressionParser.cs | 2 +- .../Networks => Model}/HardwareProfile.cs | 4 +- SNP_Evolution/Model/Model.csproj | 7 + .../Networks => Model}/Network.cs | 2 +- .../Networks => Model}/NetworkEdits.cs | 4 +- .../Networks => Model}/NetworkGraph.cs | 2 +- .../Networks => Model}/NetworkNotation.cs | 2 +- .../Networks => Model}/NetworkPage.cs | 2 +- .../Networks => Model}/Neuron.cs | 2 +- .../Networks => Model}/PositionAutomaton.cs | 4 +- .../Networks => Model}/ReferenceNetworks.cs | 2 +- .../{SNP_Evolution/Networks => Model}/Rule.cs | 2 +- .../Networks => Model}/SpikeCondition.cs | 2 +- .../Application/BenchmarkServiceTests.cs | 20 +- .../Application/CatalogTests.cs | 7 + .../Application/ComposeSessionTests.cs | 33 +- .../Application/EvaluationAccountingTests.cs | 65 +-- .../Application/EvolveServiceTests.cs | 23 +- .../Application/ExportServiceTests.cs | 18 + .../Application/OutputTargetTests.cs | 49 +++ .../Application/PartLibrariesTests.cs | 24 +- .../Application/PartsServiceTests.cs | 14 +- .../Application/ReachServiceTests.cs | 36 +- .../Application/RunFoldersTests.cs | 21 +- .../Application/TargetPatternsTests.cs | 36 ++ .../Cli/CommandArgsTests.cs | 9 +- .../Cli/ExportCommandsTests.cs | 103 ++--- .../Cli/ListCommandsTests.cs | 3 +- .../SNP_Evolution.Tests/Cli/SavedRunsTests.cs | 12 +- .../Cli/VerifyCommandTests.cs | 84 ++-- .../Compilation/CompilerTests.cs | 213 ---------- .../Compilation/RecurrenceCompilerTests.cs | 54 +++ .../RegisterMachineCompilerTests.cs | 51 +++ .../Compilation/RegisterProgramTests.cs | 46 ++ .../Evolution/AlgorithmTests.cs | 164 ------- .../Evolution/CheckScoringTests.cs | 75 ---- .../Evolution/CompositionTests.cs | 247 ----------- .../Evolution/HardwareProfileTests.cs | 176 -------- .../Evolution/MultiplicationTests.cs | 71 ---- .../Evolution/OutputTargetTests.cs | 142 ------- .../Evolution/PartWiringTests.cs | 274 ------------ .../Evolution/SearchTests.cs | 268 ------------ .../Evolution/StructuralOperatorTests.cs | 154 ------- .../Evolution/TaskTests.cs | 115 ----- .../Export/NirExporterTests.cs | 26 +- .../Export/UppaalExporterTests.cs | 25 +- .../Export/VerilogExporterTests.cs | 52 +-- .../Fixtures/BenchmarkFixtures.cs | 10 + .../Fixtures/CompositionFixtures.cs | 28 ++ .../Fixtures/ConsoleCapture.cs | 35 ++ .../Fixtures/DelegateEvaluator.cs | 15 + .../SNP_Evolution.Tests/Fixtures/Engines.cs | 12 + .../SNP_Evolution.Tests/Fixtures/Factories.cs | 16 + .../Fixtures/GeneticAlgorithms.cs | 14 + .../{Evolution => Fixtures}/ModuleFixtures.cs | 26 +- .../{ => Fixtures}/PartFixtures.cs | 30 +- .../Fixtures/PartWiringFixtures.cs | 41 ++ .../RecordingEvaluator.cs | 6 +- .../{ => Fixtures}/RepositoryFiles.cs | 7 +- .../SNP_Evolution.Tests/Fixtures/Runs.cs | 35 ++ .../SNP_Evolution.Tests/Fixtures/Sequences.cs | 7 + .../{ => Fixtures}/SlowAttribute.cs | 4 +- .../{ => Fixtures}/SlowAttributeTests.cs | 11 +- .../Fixtures/StubAlgorithm.cs | 30 ++ .../Fixtures/TempFolder.cs | 28 ++ .../{ => Fixtures}/TestNetworks.cs | 6 +- .../Fixtures/TextDifference.cs | 22 + .../Fixtures/TextDifferenceTests.cs | 13 + .../ToolFactAttributes.cs | 16 +- .../Fixtures/TrialResults.cs | 10 + .../Fixtures/WellFormedNetworks.cs | 32 ++ .../Golden/CommandGoldenTests.cs | 5 +- .../SNP_Evolution.Tests/Golden/CommandRun.cs | 37 +- .../Golden/ContractGoldenTests.cs | 10 +- .../Golden/EngineTraceGoldenTests.cs | 10 +- .../SNP_Evolution.Tests/Golden/GoldenFile.cs | 19 +- .../Golden/GoldenFileTests.cs | 13 - .../Layering/InterfaceLayerTests.cs | 26 -- .../SNP_Evolution.Tests/Layering/Layers.cs | 15 + .../Layering/NamespaceCycleTests.cs | 4 +- .../Layering/NamespaceGraph.cs | 10 +- .../Layering/ProjectLayerTests.cs | 60 +++ .../Model/HardwareProfileTests.cs | 77 ++++ .../Model/NetworkEditsTests.cs | 24 ++ .../{Networks => Model}/NetworkGraphTests.cs | 4 +- .../NetworkNotationTests.cs | 4 +- .../{Networks => Model}/NetworkPageTests.cs | 4 +- .../{Networks => Model}/RuleTests.cs | 4 +- .../SpikeConditionTests.cs | 4 +- .../{Networks => Model}/StandardRuleTests.cs | 27 +- .../SNP_Evolution.Tests.csproj | 5 +- .../Algorithms}/GeneticAlgorithmTests.cs | 36 +- .../Search/Algorithms/ImmigrantsTests.cs | 91 ++++ .../Algorithms/IterativeEvolutionTests.cs | 59 +++ .../Search/Algorithms/MapElitesTests.cs | 58 +++ .../Algorithms/MuPlusLambdaStrategyTests.cs | 23 + .../Search/Algorithms/RankingTests.cs | 24 ++ .../Algorithms/SpeciatedAlgorithmTests.cs | 17 + .../Algorithms/StagnationRecoveryTests.cs | 42 ++ .../Algorithms}/StallDetectorTests.cs | 6 +- .../Search/Benchmarking/BenchmarkTests.cs | 74 ++++ .../Search/Benchmarking/TaskSuiteTests.cs | 21 + .../Search/CompositionSearchTests.cs | 148 +++++++ .../Search/EvolutionContextTests.cs | 70 +++ .../Search/EvolutionSearchTests.cs | 72 ++++ .../Search/Fitness/CheckDiagnosisTests.cs | 20 + .../Fitness}/FitnessEvaluatorTests.cs | 49 ++- .../Search/Genome/NetworkFactoryTests.cs | 34 ++ .../Genome}/NetworkGenerationTests.cs | 6 +- .../Modules}/HandBuiltMachinesTests.cs | 42 +- .../Modules}/ModularEvolutionTests.cs | 42 +- .../Search/Modules/ModuleMutationsTests.cs | 67 +++ .../Modules/ModuleTrackerTests.cs} | 44 +- .../Search/Modules/PartMutationsTests.cs | 106 +++++ .../Modules}/PromotionTests.cs | 37 +- .../Operators/LexicaseSelectionTests.cs | 31 ++ .../Operators/StructuralMutationsTests.cs | 90 ++++ .../Search/PartSearchTests.cs | 36 ++ .../Search/ProgramSearchTests.cs | 51 +++ .../Proposals/PartProposalsTests.cs} | 82 +--- .../Proposals/RecurrenceProposerTests.cs | 42 ++ .../SearchCatalogTests.cs | 20 +- .../Search/ShrinkSearchTests.cs | 54 +++ .../Search/WeightedMutationTests.cs | 31 ++ .../Simulation/AxonalDelayTests.cs | 26 +- .../Simulation/ExhaustiveEngineTests.cs | 4 +- .../Simulation/JitterTests.cs | 4 +- .../{ => Metal}/MetalEngineTests.cs | 20 +- .../Simulation/NetworkRunnerTests.cs | 2 +- .../Simulation/NetworkSimulationTests.cs | 4 +- .../Simulation/PortReadoutTests.cs | 13 +- .../Simulation/RoutedEngineTests.cs | 2 +- .../Simulation/SimulationEngineTests.cs | 6 +- .../Simulation/SpikeTrainTests.cs | 4 +- .../Simulation/StandardSemanticsTests.cs | 8 +- .../Accounting}/EvaluationBudgetTests.cs | 13 +- .../Accounting}/StatisticsTests.cs | 4 +- .../Contracts}/ArithmeticPartsTests.cs | 22 +- .../Contracts}/ContractTests.cs | 4 +- .../Contracts}/FirstPartsTests.cs | 27 +- .../Contracts}/PortEncodingTests.cs | 12 +- .../Specs/Parts/CompositionTests.cs | 93 ++++ .../Parts}/FibonacciCompositionTests.cs | 29 +- .../Parts}/HardwareCostTests.cs | 22 +- .../Parts}/ModuleCutTests.cs | 11 +- .../Parts}/ModuleEditTests.cs | 49 +-- .../Specs/Parts/ModuleLibraryTests.cs | 27 ++ .../Parts}/PartLibraryTests.cs | 28 +- .../Specs/Parts/PartWiringTests.cs | 131 ++++++ .../Specs/Parts/StopReasonTests.cs | 23 + .../Specs/Tasks/AcceptorTaskTests.cs | 37 ++ .../Tasks}/ContractTaskTests.cs | 50 ++- .../Specs/Tasks/CurriculumPlanTests.cs | 22 + .../Tasks}/FitnessFunctionTests.cs | 4 +- .../Specs/Tasks/FunctionTaskTests.cs | 50 +++ .../Specs/Tasks/GeneratorTaskTests.cs | 22 + .../Specs/Tasks/SequenceTaskTests.cs | 101 +++++ .../Specs/Tasks/SolvedTests.cs | 18 + .../Specs/Tasks/SpikeWordTaskTests.cs | 39 ++ .../Tasks}/StreamingTaskTests.cs | 29 +- .../Tasks}/TaskScoringTests.cs | 19 +- .../Verification}/BoundedCheckTests.cs | 69 +-- .../Verification}/RobustnessTests.cs | 28 +- .../Storage/NetworkFilesTests.cs | 37 +- .../Storage/PartLibraryFilesTests.cs | 39 +- SNP_Evolution/SNP_Evolution.slnx | 10 +- .../SNP_Evolution/SNP_Evolution.csproj | 27 -- .../obj/SNP_Evolution.csproj.nuget.g.props | 15 - .../obj/SNP_Evolution.csproj.nuget.g.targets | 2 - .../SNP_Evolution/obj/project.assets.json | 402 ------------------ .../Algorithms/GenerationLoop.cs | 4 +- .../Algorithms/GeneticAlgorithm.cs | 8 +- .../Algorithms/IGeneticAlgorithm.cs | 6 +- .../Algorithms/Immigrants.cs | 6 +- .../Algorithms/IterativeEvolution.cs | 8 +- .../Algorithms/MapElites.cs | 8 +- .../Algorithms/MuPlusLambdaStrategy.cs | 8 +- .../Algorithms/Ranking.cs | 4 +- .../Algorithms/ScoreHistory.cs | 4 +- .../Algorithms/SolveCheck.cs | 6 +- .../Algorithms/SpeciatedAlgorithm.cs | 8 +- .../Algorithms/StagnationRecovery.cs | 8 +- .../Algorithms/StallDetector.cs | 2 +- .../Benchmarking/AlgorithmSelector.cs | 8 +- .../Benchmarking/Benchmark.cs | 22 +- .../Benchmarking/TaskSuite.cs | 8 +- .../Evolution => }/Search/CompositionSpace.cs | 18 +- .../Evolution => }/Search/EvolutionContext.cs | 16 +- .../Evolution => }/Search/EvolutionSearch.cs | 12 +- .../Fitness/CheckDiagnosis.cs | 4 +- .../Fitness/FitnessEvaluator.cs | 8 +- .../Fitness/Individual.cs | 4 +- .../Genome/ExpressionGenerator.cs | 2 +- .../Genome/NetworkFactory.cs | 4 +- .../Genome/RandomTopology.cs | 4 +- .../Evolution => }/Search/ISearch.cs | 8 +- .../Modules/CompositionEdits.cs | 8 +- .../Modules/HandBuiltMachines.cs | 12 +- .../Modules/ModularEvolution.cs | 12 +- .../Modules/ModuleMutations.cs | 12 +- .../Modules/ModuleSupport.cs | 4 +- .../Modules/ModuleTracker.cs | 12 +- .../Modules/PartMutations.cs | 12 +- .../Evolution => Search}/Modules/Promotion.cs | 14 +- .../Evolution => }/Search/NetworkSetup.cs | 18 +- .../Operators/GeneticOperators.cs | 6 +- .../Operators/LexicaseSelection.cs | 6 +- .../Operators/MutationPressure.cs | 2 +- .../Operators/NeuronCrossover.cs | 4 +- .../Operators/ProfileOperators.cs | 6 +- .../Operators/RouletteWheelSelection.cs | 4 +- .../Operators/RuleExpressionCrossover.cs | 4 +- .../Operators/RuleExpressionMutation.cs | 4 +- .../Operators/ShrinkEdits.cs | 4 +- .../Operators/StructuralMutations.cs | 6 +- .../Operators/TournamentSelection.cs | 4 +- .../Operators/WeightedEdit.cs | 2 +- .../Evolution => }/Search/PartSearch.cs | 20 +- .../Evolution => }/Search/ProgramEdits.cs | 2 +- .../Evolution => }/Search/ProgramScoring.cs | 8 +- .../Evolution => }/Search/ProgramSearch.cs | 10 +- .../Proposals/PartProposals.cs | 22 +- .../Proposals/RecurrenceProposer.cs | 4 +- .../Search/RandomComposition.cs | 10 +- SNP_Evolution/Search/Search.csproj | 8 + .../Evolution => }/Search/SearchCatalog.cs | 10 +- .../Evolution => }/Search/ShrinkSearch.cs | 20 +- .../Evolution => }/Search/WeightedMutation.cs | 10 +- .../Simulation/AxonBuffer.cs | 0 .../Simulation/CompiledNetwork.cs | 2 +- .../Simulation/EngineSupport.cs | 0 .../Simulation/ExhaustiveCpuEngine.cs | 0 .../Simulation/ISimulationEngine.cs | 2 +- .../Simulation/InputSpikes.cs | 0 .../Simulation/JitterBuffer.cs | 0 .../Simulation/Metal/GpuBatch.cs | 2 +- .../Simulation/Metal/GpuNetwork.cs | 0 .../Simulation/Metal/MetalBuffers.cs | 0 .../Simulation/Metal/MetalContext.cs | 0 .../Simulation/Metal/MetalEngine.cs | 0 .../Simulation/Metal/ObjectiveC.cs | 0 .../Simulation/Metal/Simulation.metal | 0 .../Simulation/NetworkRunner.cs | 2 +- .../Simulation/NetworkSimulation.cs | 2 +- .../Simulation/NetworkStep.cs | 2 +- .../Simulation/NeuronStates.cs | 0 .../Simulation/OutputDecoder.cs | 0 .../Simulation/OutputTiming.cs | 0 .../Simulation/ParallelCpuEngine.cs | 0 .../Simulation/PortRecorder.cs | 0 .../Simulation/RoutedEngine.cs | 0 .../Simulation/Sampling.cs | 0 .../Simulation/SequentialCpuEngine.cs | 0 SNP_Evolution/Simulation/Simulation.csproj | 15 + .../Simulation/SimulationOptions.cs | 0 .../Simulation/SpikeRelease.cs | 0 .../Simulation/StateKey.cs | 0 .../{SNP_Evolution => }/Simulation/Trial.cs | 2 +- .../Simulation/TrialResult.cs | 0 .../Accounting/EvaluationBudget.cs | 2 +- .../Accounting/SearchOutcome.cs | 2 +- .../Accounting/Statistics.cs | 2 +- .../Contracts/ArithmeticParts.cs | 4 +- .../Contracts/CatalogueEntry.cs | 2 +- .../Evolution => Specs}/Contracts/Contract.cs | 2 +- .../Contracts/FirstParts.cs | 4 +- .../Contracts/PortBinding.cs | 2 +- .../Contracts/PortEncoding.cs | 2 +- .../Contracts/PortLayout.cs | 2 +- .../Contracts/Specification.cs | 2 +- .../Contracts/Specifications.cs | 2 +- .../Evolution => Specs}/Parts/Composition.cs | 6 +- .../Parts/HandBuiltParts.cs | 8 +- .../Evolution => Specs}/Parts/HardwareCost.cs | 4 +- .../Evolution => Specs}/Parts/LibraryPart.cs | 4 +- .../Evolution => Specs}/Parts/ModuleCuts.cs | 5 +- .../Evolution => Specs}/Parts/ModuleEdits.cs | 6 +- .../Parts/ModuleLibrary.cs | 6 +- .../Evolution => Specs}/Parts/Part.cs | 8 +- .../Evolution => Specs}/Parts/PartRecipe.cs | 6 +- .../Evolution => Specs}/Parts/PartReuse.cs | 4 +- .../Evolution => Specs}/Parts/PartWiring.cs | 6 +- .../Evolution => Specs}/Parts/ProvenBound.cs | 2 +- .../Parts/ReferenceParts.cs | 6 +- .../Evolution => Specs}/Parts/StopReason.cs | 2 +- SNP_Evolution/Specs/Specs.csproj | 7 + .../Evolution => Specs}/Tasks/AcceptorTask.cs | 2 +- .../Evolution => Specs}/Tasks/CloseCredit.cs | 2 +- .../Tasks/ContractPorts.cs | 4 +- .../Tasks/ContractScoring.cs | 4 +- .../Evolution => Specs}/Tasks/ContractTask.cs | 4 +- .../Evolution => Specs}/Tasks/FunctionTask.cs | 2 +- .../Tasks/GeneratorTask.cs | 2 +- .../Tasks/IFitnessFunction.cs | 2 +- .../Evolution => Specs}/Tasks/ITask.cs | 4 +- .../Tasks/JaccardFitness.cs | 2 +- .../Evolution => Specs}/Tasks/SequenceTask.cs | 4 +- .../Tasks/SetCoverageFitness.cs | 2 +- .../Evolution => Specs}/Tasks/Solved.cs | 2 +- .../Evolution => Specs}/Tasks/SpikeTrains.cs | 2 +- .../Tasks/SpikeWordTask.cs | 2 +- .../Tasks/StreamingTask.cs | 2 +- .../Tasks/TaskAbilities.cs | 4 +- .../Evolution => Specs}/Tasks/TaskScoring.cs | 2 +- .../Tasks/TriggeredSequenceTask.cs | 2 +- .../Verification/BoundedCheck.cs | 10 +- .../Verification/BoundedInputs.cs | 4 +- .../Verification/CounterexampleText.cs | 8 +- .../Verification/PartMeasurement.cs | 6 +- .../Verification/Robustness.cs | 10 +- .../Verification/SpikeTrace.cs | 8 +- .../Verification/Verdict.cs | 8 +- .../Verification/Verifier.cs | 10 +- .../{SNP_Evolution => }/Storage/FitnessCsv.cs | 0 .../{SNP_Evolution => }/Storage/Json.cs | 2 +- .../Storage/NetworkFiles.cs | 2 +- .../Storage/PartLibraryFiles.cs | 10 +- .../Storage/SpikeCountJsonConverter.cs | 0 .../Storage/StopReasonJsonConverter.cs | 2 +- SNP_Evolution/Storage/Storage.csproj | 7 + install-snp.sh | 2 +- 442 files changed, 4106 insertions(+), 4071 deletions(-) create mode 100644 .github/tickets/where.py create mode 100644 SNP_Evolution/Application/Application.csproj rename SNP_Evolution/{SNP_Evolution => }/Application/BenchmarkService.cs (88%) rename SNP_Evolution/{SNP_Evolution => }/Application/Catalog.cs (93%) rename SNP_Evolution/{SNP_Evolution => }/Application/CompileService.cs (95%) rename SNP_Evolution/{SNP_Evolution => }/Application/ComposeService.cs (92%) rename SNP_Evolution/{SNP_Evolution => }/Application/EngineChoice.cs (82%) rename SNP_Evolution/{SNP_Evolution => }/Application/EvolutionSession.cs (95%) rename SNP_Evolution/{SNP_Evolution => }/Application/EvolveService.cs (83%) rename SNP_Evolution/{SNP_Evolution => }/Application/ExportService.cs (94%) rename SNP_Evolution/{SNP_Evolution => }/Application/Loaded.cs (92%) rename SNP_Evolution/{SNP_Evolution => }/Application/NetworkRunService.cs (85%) rename SNP_Evolution/{SNP_Evolution => }/Application/OutputTarget.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Application/PartLibraries.cs (94%) rename SNP_Evolution/{SNP_Evolution => }/Application/PartsService.cs (92%) rename SNP_Evolution/{SNP_Evolution => }/Application/ReachService.cs (96%) rename SNP_Evolution/{SNP_Evolution => }/Application/RunAdvisor.cs (96%) rename SNP_Evolution/{SNP_Evolution => }/Application/RunFolders.cs (97%) rename SNP_Evolution/{SNP_Evolution => }/Application/RunLayers.cs (89%) rename SNP_Evolution/{SNP_Evolution => }/Application/RunNotes.cs (96%) rename SNP_Evolution/{SNP_Evolution => }/Application/RunOutput.cs (95%) rename SNP_Evolution/{SNP_Evolution => }/Application/RunSeed.cs (92%) rename SNP_Evolution/{SNP_Evolution => }/Application/Settings.cs (94%) rename SNP_Evolution/{SNP_Evolution => }/Application/TargetPatterns.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Application/VerifyService.cs (83%) rename SNP_Evolution/{SNP_Evolution => }/Cli/BenchmarkMenu.cs (96%) create mode 100644 SNP_Evolution/Cli/Cli.csproj rename SNP_Evolution/{SNP_Evolution => }/Cli/CommandLine.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/AdviseCommand.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/BenchmarkArgs.cs (95%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/Command.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/CommandArgs.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/CommandRegistry.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/CommonOptions.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/CompileCommand.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/ComposeCommand.cs (97%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/EvolveCommand.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/EvolvePartsCommand.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/ExportCommands.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/ListCommands.cs (92%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/Option.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/ReachCommand.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/TargetArgs.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/ValueKind.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/Commands/VerifyCommand.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Cli/CompileMenu.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/ConsoleInput.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/ConsoleUi.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/EvolveMenu.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Cli/InputParsing.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/MainMenu.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/MenuPrompts.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/PartsMenu.cs (97%) rename SNP_Evolution/{SNP_Evolution => Cli}/Program.cs (96%) rename SNP_Evolution/{SNP_Evolution => }/Cli/RedoMenu.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/RunMenu.cs (97%) rename SNP_Evolution/{SNP_Evolution => }/Cli/SavedRuns.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Cli/SearchMenu.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/SettingOptions.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Cli/SettingsMenu.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Cli/SettingsPage.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Cli/TargetMenu.cs (100%) create mode 100644 SNP_Evolution/Compilation/Compilation.csproj rename SNP_Evolution/{SNP_Evolution => }/Compilation/Recurrence.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Compilation/RecurrenceCompiler.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Compilation/RegisterMachine.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Compilation/RegisterMachineCompiler.cs (99%) create mode 100644 SNP_Evolution/Export/Export.csproj rename SNP_Evolution/{SNP_Evolution => }/Export/ExternalTool.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Export/NetworkPort.cs (92%) rename SNP_Evolution/{SNP_Evolution => }/Export/NirExporter.cs (97%) rename SNP_Evolution/{SNP_Evolution => }/Export/UppaalDeclarations.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Export/UppaalExporter.cs (97%) rename SNP_Evolution/{SNP_Evolution => }/Export/VerilogExporter.cs (97%) rename SNP_Evolution/{SNP_Evolution => }/Export/VerilogNeuronModules.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Export/VerilogTestbench.cs (98%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/DelayKind.cs (95%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/ExpressionParser.cs (99%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/HardwareProfile.cs (98%) create mode 100644 SNP_Evolution/Model/Model.csproj rename SNP_Evolution/{SNP_Evolution/Networks => Model}/Network.cs (97%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/NetworkEdits.cs (96%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/NetworkGraph.cs (99%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/NetworkNotation.cs (98%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/NetworkPage.cs (99%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/Neuron.cs (98%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/PositionAutomaton.cs (98%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/ReferenceNetworks.cs (98%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/Rule.cs (99%) rename SNP_Evolution/{SNP_Evolution/Networks => Model}/SpikeCondition.cs (99%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Application/ExportServiceTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Application/OutputTargetTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Application/TargetPatternsTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Compilation/CompilerTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Compilation/RecurrenceCompilerTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterMachineCompilerTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/AlgorithmTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/CheckScoringTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/CompositionTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/HardwareProfileTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/MultiplicationTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/OutputTargetTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/PartWiringTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/SearchTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/StructuralOperatorTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Evolution/TaskTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/BenchmarkFixtures.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/CompositionFixtures.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/ConsoleCapture.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/DelegateEvaluator.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/Engines.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/Factories.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/GeneticAlgorithms.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Fixtures}/ModuleFixtures.cs (60%) rename SNP_Evolution/SNP_Evolution.Tests/{ => Fixtures}/PartFixtures.cs (61%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/PartWiringFixtures.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Fixtures}/RecordingEvaluator.cs (88%) rename SNP_Evolution/SNP_Evolution.Tests/{ => Fixtures}/RepositoryFiles.cs (76%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/Runs.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/Sequences.cs rename SNP_Evolution/SNP_Evolution.Tests/{ => Fixtures}/SlowAttribute.cs (86%) rename SNP_Evolution/SNP_Evolution.Tests/{ => Fixtures}/SlowAttributeTests.cs (75%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/StubAlgorithm.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/TempFolder.cs rename SNP_Evolution/SNP_Evolution.Tests/{ => Fixtures}/TestNetworks.cs (91%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/TextDifference.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/TextDifferenceTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Export => Fixtures}/ToolFactAttributes.cs (73%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/TrialResults.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/WellFormedNetworks.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenFileTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Layering/InterfaceLayerTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Layering/Layers.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Layering/ProjectLayerTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Model/HardwareProfileTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Model/NetworkEditsTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Networks => Model}/NetworkGraphTests.cs (92%) rename SNP_Evolution/SNP_Evolution.Tests/{Networks => Model}/NetworkNotationTests.cs (94%) rename SNP_Evolution/SNP_Evolution.Tests/{Networks => Model}/NetworkPageTests.cs (90%) rename SNP_Evolution/SNP_Evolution.Tests/{Networks => Model}/RuleTests.cs (87%) rename SNP_Evolution/SNP_Evolution.Tests/{Networks => Model}/SpikeConditionTests.cs (97%) rename SNP_Evolution/SNP_Evolution.Tests/{Networks => Model}/StandardRuleTests.cs (76%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Search/Algorithms}/GeneticAlgorithmTests.cs (76%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/ImmigrantsTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/IterativeEvolutionTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/MapElitesTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/MuPlusLambdaStrategyTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/RankingTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/SpeciatedAlgorithmTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/StagnationRecoveryTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Search/Algorithms}/StallDetectorTests.cs (95%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/BenchmarkTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/TaskSuiteTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/CompositionSearchTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/EvolutionContextTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/EvolutionSearchTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Fitness/CheckDiagnosisTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Search/Fitness}/FitnessEvaluatorTests.cs (78%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Genome/NetworkFactoryTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Search/Genome}/NetworkGenerationTests.cs (95%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Search/Modules}/HandBuiltMachinesTests.cs (55%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Search/Modules}/ModularEvolutionTests.cs (78%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModuleMutationsTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution/ModuleLibraryTests.cs => Search/Modules/ModuleTrackerTests.cs} (55%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Modules/PartMutationsTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Search/Modules}/PromotionTests.cs (87%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Operators/LexicaseSelectionTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Operators/StructuralMutationsTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/PartSearchTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/ProgramSearchTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution/ProposalTests.cs => Search/Proposals/PartProposalsTests.cs} (53%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Proposals/RecurrenceProposerTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Search}/SearchCatalogTests.cs (86%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/ShrinkSearchTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/WeightedMutationTests.cs rename SNP_Evolution/SNP_Evolution.Tests/Simulation/{ => Metal}/MetalEngineTests.cs (91%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Accounting}/EvaluationBudgetTests.cs (95%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Accounting}/StatisticsTests.cs (95%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Contracts}/ArithmeticPartsTests.cs (86%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Contracts}/ContractTests.cs (98%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Contracts}/FirstPartsTests.cs (80%) rename SNP_Evolution/SNP_Evolution.Tests/{Simulation => Specs/Contracts}/PortEncodingTests.cs (88%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/CompositionTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Parts}/FibonacciCompositionTests.cs (89%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Parts}/HardwareCostTests.cs (86%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Parts}/ModuleCutTests.cs (86%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Parts}/ModuleEditTests.cs (61%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleLibraryTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Parts}/PartLibraryTests.cs (67%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/PartWiringTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/StopReasonTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/AcceptorTaskTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Tasks}/ContractTaskTests.cs (84%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/CurriculumPlanTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Tasks}/FitnessFunctionTests.cs (95%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/FunctionTaskTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/GeneratorTaskTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SequenceTaskTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SolvedTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SpikeWordTaskTests.cs rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Tasks}/StreamingTaskTests.cs (73%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Tasks}/TaskScoringTests.cs (65%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Verification}/BoundedCheckTests.cs (69%) rename SNP_Evolution/SNP_Evolution.Tests/{Evolution => Specs/Verification}/RobustnessTests.cs (61%) delete mode 100644 SNP_Evolution/SNP_Evolution/SNP_Evolution.csproj delete mode 100644 SNP_Evolution/SNP_Evolution/obj/SNP_Evolution.csproj.nuget.g.props delete mode 100644 SNP_Evolution/SNP_Evolution/obj/SNP_Evolution.csproj.nuget.g.targets delete mode 100644 SNP_Evolution/SNP_Evolution/obj/project.assets.json rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/GenerationLoop.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/GeneticAlgorithm.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/IGeneticAlgorithm.cs (90%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/Immigrants.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/IterativeEvolution.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/MapElites.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/MuPlusLambdaStrategy.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/Ranking.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/ScoreHistory.cs (88%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/SolveCheck.cs (88%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/SpeciatedAlgorithm.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/StagnationRecovery.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Algorithms/StallDetector.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Benchmarking/AlgorithmSelector.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Benchmarking/Benchmark.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Benchmarking/TaskSuite.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/CompositionSpace.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/EvolutionContext.cs (84%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/EvolutionSearch.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Fitness/CheckDiagnosis.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Fitness/FitnessEvaluator.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Fitness/Individual.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Genome/ExpressionGenerator.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Genome/NetworkFactory.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Genome/RandomTopology.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/ISearch.cs (88%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Modules/CompositionEdits.cs (91%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Modules/HandBuiltMachines.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Modules/ModularEvolution.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Modules/ModuleMutations.cs (91%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Modules/ModuleSupport.cs (82%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Modules/ModuleTracker.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Modules/PartMutations.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Modules/Promotion.cs (92%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/NetworkSetup.cs (76%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/GeneticOperators.cs (87%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/LexicaseSelection.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/MutationPressure.cs (90%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/NeuronCrossover.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/ProfileOperators.cs (88%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/RouletteWheelSelection.cs (92%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/RuleExpressionCrossover.cs (93%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/RuleExpressionMutation.cs (93%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/ShrinkEdits.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/StructuralMutations.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/TournamentSelection.cs (89%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Operators/WeightedEdit.cs (78%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/PartSearch.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/ProgramEdits.cs (99%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/ProgramScoring.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/ProgramSearch.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Proposals/PartProposals.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Search}/Proposals/RecurrenceProposer.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/RandomComposition.cs (91%) create mode 100644 SNP_Evolution/Search/Search.csproj rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/SearchCatalog.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/ShrinkSearch.cs (93%) rename SNP_Evolution/{SNP_Evolution/Evolution => }/Search/WeightedMutation.cs (96%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/AxonBuffer.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/CompiledNetwork.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/EngineSupport.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/ExhaustiveCpuEngine.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/ISimulationEngine.cs (97%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/InputSpikes.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/JitterBuffer.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Metal/GpuBatch.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Metal/GpuNetwork.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Metal/MetalBuffers.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Metal/MetalContext.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Metal/MetalEngine.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Metal/ObjectiveC.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Metal/Simulation.metal (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/NetworkRunner.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/NetworkSimulation.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/NetworkStep.cs (99%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/NeuronStates.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/OutputDecoder.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/OutputTiming.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/ParallelCpuEngine.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/PortRecorder.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/RoutedEngine.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Sampling.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/SequentialCpuEngine.cs (100%) create mode 100644 SNP_Evolution/Simulation/Simulation.csproj rename SNP_Evolution/{SNP_Evolution => }/Simulation/SimulationOptions.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/SpikeRelease.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/StateKey.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/Trial.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Simulation/TrialResult.cs (100%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Accounting/EvaluationBudget.cs (99%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Accounting/SearchOutcome.cs (93%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Accounting/Statistics.cs (99%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/ArithmeticParts.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/CatalogueEntry.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/Contract.cs (99%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/FirstParts.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/PortBinding.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/PortEncoding.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/PortLayout.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/Specification.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Contracts/Specifications.cs (99%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/Composition.cs (99%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/HandBuiltParts.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/HardwareCost.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/LibraryPart.cs (90%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/ModuleCuts.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/ModuleEdits.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/ModuleLibrary.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/Part.cs (79%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/PartRecipe.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/PartReuse.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/PartWiring.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/ProvenBound.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/ReferenceParts.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Parts/StopReason.cs (98%) create mode 100644 SNP_Evolution/Specs/Specs.csproj rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/AcceptorTask.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/CloseCredit.cs (90%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/ContractPorts.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/ContractScoring.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/ContractTask.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/FunctionTask.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/GeneratorTask.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/IFitnessFunction.cs (86%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/ITask.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/JaccardFitness.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/SequenceTask.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/SetCoverageFitness.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/Solved.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/SpikeTrains.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/SpikeWordTask.cs (99%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/StreamingTask.cs (99%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/TaskAbilities.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/TaskScoring.cs (97%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Tasks/TriggeredSequenceTask.cs (98%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Verification/BoundedCheck.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Verification/BoundedInputs.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Verification/CounterexampleText.cs (95%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Verification/PartMeasurement.cs (86%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Verification/Robustness.cs (94%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Verification/SpikeTrace.cs (96%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Verification/Verdict.cs (89%) rename SNP_Evolution/{SNP_Evolution/Evolution => Specs}/Verification/Verifier.cs (94%) rename SNP_Evolution/{SNP_Evolution => }/Storage/FitnessCsv.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Storage/Json.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Storage/NetworkFiles.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Storage/PartLibraryFiles.cs (98%) rename SNP_Evolution/{SNP_Evolution => }/Storage/SpikeCountJsonConverter.cs (100%) rename SNP_Evolution/{SNP_Evolution => }/Storage/StopReasonJsonConverter.cs (95%) create mode 100644 SNP_Evolution/Storage/Storage.csproj diff --git a/.github/tickets/README.md b/.github/tickets/README.md index 41d89a1..1c3dfc4 100644 --- a/.github/tickets/README.md +++ b/.github/tickets/README.md @@ -40,8 +40,11 @@ carries what the agent needs: 3. **`Its responsibilities are:`** only where there are distinct ones. 4. **`should`** for each requirement, with the reason straight after it. 5. **`Where:`** the files and types to start from. Paths are relative to the - solution folder `SNP_Evolution/`: `SNP_Evolution/...` is the app and - `SNP_Evolution.Tests/...` the tests. + solution folder `SNP_Evolution/`, whose folders are the layer projects + (`Model/...`, `Specs/...`, `Search/...` and so on, as the README lists them) + and `SNP_Evolution.Tests/...` the tests; a file the ticket creates follows + the word "new". `python3 .github/tickets/where.py` checks that every + path in an open ticket's `Where:` line exists. 6. **`Done when:`** a checklist that a reviewer can tick without asking. 7. **`Read first:`** the spec sections, last line, no trailing period. diff --git a/.github/tickets/m2.5/m2.5-p6-1-test-fixtures.md b/.github/tickets/m2.5/m2.5-p6-1-test-fixtures.md index 7618b70..fbbfaa8 100644 --- a/.github/tickets/m2.5/m2.5-p6-1-test-fixtures.md +++ b/.github/tickets/m2.5/m2.5-p6-1-test-fixtures.md @@ -18,7 +18,7 @@ Its responsibilities are: Tests should keep their assertions, because this is a move; a test that changes what it checks belongs in another ticket. -Where: `SNP_Evolution.Tests/`, `SNP_Evolution.Tests/TestNetworks.cs`, `SNP_Evolution.Tests/Evolution/ModuleFixtures.cs`. +Where: `SNP_Evolution.Tests/`, `SNP_Evolution.Tests/Fixtures/TestNetworks.cs`, `SNP_Evolution.Tests/Fixtures/ModuleFixtures.cs`. Done when: - [ ] No builder or setup is defined in more than one test file diff --git a/.github/tickets/m2.5/m2.5-p6-2-project-split.md b/.github/tickets/m2.5/m2.5-p6-2-project-split.md index 97a4e7b..d728341 100644 --- a/.github/tickets/m2.5/m2.5-p6-2-project-split.md +++ b/.github/tickets/m2.5/m2.5-p6-2-project-split.md @@ -19,7 +19,7 @@ Its responsibilities are: The exact cut may move a type to a lower layer where a reference would otherwise point upwards, and each such move should be listed, because the cut is right only where the dependencies agree with it. `install-snp.sh` and `dotnet tool install` should work as before, because that is how the tool is used. -Where: `SNP_Evolution.slnx`, `SNP_Evolution/SNP_Evolution.csproj`, `SNP_Evolution.Tests/SNP_Evolution.Tests.csproj`, `Directory.Build.props`, `install-snp.sh`. +Where: `SNP_Evolution.slnx`, `Cli/Cli.csproj`, `SNP_Evolution.Tests/SNP_Evolution.Tests.csproj`, `Directory.Build.props`, `install-snp.sh`. Done when: - [ ] The solution builds as separate projects and no project references one above it diff --git a/.github/tickets/m2/m2-p3-1-streaming-tasks.md b/.github/tickets/m2/m2-p3-1-streaming-tasks.md index b9a0683..57ae71a 100644 --- a/.github/tickets/m2/m2-p3-1-streaming-tasks.md +++ b/.github/tickets/m2/m2-p3-1-streaming-tasks.md @@ -18,7 +18,7 @@ Its responsibilities are: The task should use the existing `Readout.SpikeTrain`, because it already returns every firing step of the output neuron. Inputs should be generated with a fixed seed per case, because the exhaustive engine needs the same input every time. -Where: new `SNP_Evolution/Evolution/Tasks/StreamingTask.cs`; `SNP_Evolution/Evolution/Tasks/SpikeTrains.cs` and `TaskSuite.cs`; `SNP_Evolution/Simulation/InputSpikes.cs`. +Where: new `Specs/Tasks/StreamingTask.cs`; `Specs/Tasks/SpikeTrains.cs` and `Search/Benchmarking/TaskSuite.cs`; `Simulation/InputSpikes.cs`. Done when: - [ ] Both tasks appear in `dotnet run -- tasks` diff --git a/.github/tickets/m2/m2-p3-2-timing-robustness.md b/.github/tickets/m2/m2-p3-2-timing-robustness.md index 9ebabd5..bcdc6be 100644 --- a/.github/tickets/m2/m2-p3-2-timing-robustness.md +++ b/.github/tickets/m2/m2-p3-2-timing-robustness.md @@ -14,7 +14,7 @@ We want a simulation option that delays each spike on each synapse by 0 to j ext The robustness score should be reported for every library part and be available as a MAP-Elites dimension, because robustness is a property to select for, not only to report. Jitter should be a sampled engine option and the exhaustive engine should refuse it, because the branching from every delay choice would explode the exhaustive search. -Where: `SNP_Evolution/Simulation/ISimulationEngine.cs` (`SimulationOptions`), `SNP_Evolution/Simulation/NetworkSimulation.cs`, `SNP_Evolution/Simulation/SequentialCpuEngine.cs`; the library summary from the `evolve-parts` command. +Where: `Simulation/SimulationOptions.cs`, `Simulation/NetworkSimulation.cs`, `Simulation/SequentialCpuEngine.cs`; the library summary from the `evolve-parts` command. Done when: - [ ] With j = 0 every result equals today's diff --git a/.github/tickets/m3/m3-p1-1-control-parts.md b/.github/tickets/m3/m3-p1-1-control-parts.md index 2693e52..9236fe4 100644 --- a/.github/tickets/m3/m3-p1-1-control-parts.md +++ b/.github/tickets/m3/m3-p1-1-control-parts.md @@ -20,7 +20,7 @@ Join's cases should include both orders, equal steps, and the largest gap the co Each contract should leave every neuron as it began, as every first part does, because a join left holding one spike fires early next round. The contracts should be general, not taken from the add loop, because they are goals for automatic discovery. -Where: `SNP_Evolution/Evolution/Contracts/FirstParts.cs` (catalogue and `Table()`), `SNP_Evolution/Evolution/Contracts/Specification.cs` (a specification for each), `SNP_Evolution/Evolution/Contracts/PartEvolution.cs`; tests in `SNP_Evolution.Tests/Evolution/FirstPartsTests.cs`. +Where: `Specs/Contracts/FirstParts.cs` (catalogue and `Table()`), `Specs/Contracts/Specification.cs` (a specification for each), `Search/PartSearch.cs`; tests in `SNP_Evolution.Tests/Specs/Contracts/FirstPartsTests.cs`. Done when: - [ ] The four contracts are in the catalogue with specifications that agree with their cases diff --git a/.github/tickets/m3/m3-p1-2-function-programs.md b/.github/tickets/m3/m3-p1-2-function-programs.md index 5316b6d..1fb4a80 100644 --- a/.github/tickets/m3/m3-p1-2-function-programs.md +++ b/.github/tickets/m3/m3-p1-2-function-programs.md @@ -20,7 +20,7 @@ The interpreter should follow every choice when the program has one, because a c A program should leave every register but the outputs at zero when it halts, and the search should check this, because a compiled part must end as it began. Each count contract in `FirstParts` and `ArithmeticParts.Count` should be a valid search target without special cases, because the route has to be general. -Where: `SNP_Evolution/Compilation/RegisterMachine.cs`, `SNP_Evolution/Compilation/ProgramSearch.cs`, `SNP_Evolution/Evolution/Contracts/Contract.cs`; tests in `SNP_Evolution.Tests/Compilation/CompilerTests.cs`. +Where: `Compilation/RegisterMachine.cs`, `Search/ProgramSearch.cs`, `Specs/Contracts/Contract.cs`; tests in `SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs` and `SNP_Evolution.Tests/Search/ProgramSearchTests.cs`. Done when: - [ ] Hand-written function programs for add, increment, double, fan-out, register and zero test pass their contracts' cases in the interpreter diff --git a/.github/tickets/m3/m3-p1-3-compile-programs-to-parts.md b/.github/tickets/m3/m3-p1-3-compile-programs-to-parts.md index 2bc1447..73b7301 100644 --- a/.github/tickets/m3/m3-p1-3-compile-programs-to-parts.md +++ b/.github/tickets/m3/m3-p1-3-compile-programs-to-parts.md @@ -22,7 +22,7 @@ Shrinking should keep the cheapest network that still verifies, as parts from se The part file should record that it came from a compiled program and keep the program, because the paper must say which parts came from the compiler. A `--route search|compile|both` option should choose the route (both by default for count contracts: compile, then search from the shrunk part), because the routes should be comparable. -Where: `SNP_Evolution/Compilation/RegisterMachineCompiler.cs`, `SNP_Evolution/Evolution/Contracts/PartEvolution.cs`, `SNP_Evolution/Evolution/Contracts/PortBinding.cs`, `SNP_Evolution/Storage/PartLibraryFiles.cs`, `SNP_Evolution/Cli/PartsSession.cs`. +Where: `Compilation/RegisterMachineCompiler.cs`, `Search/PartSearch.cs`, `Specs/Contracts/PortBinding.cs`, `Storage/PartLibraryFiles.cs`, `Application/PartsService.cs`. Done when: - [ ] The hand-written function programs for register, add, increment, fan-out and zero test compile to parts that pass their contracts on the exhaustive engine diff --git a/.github/tickets/m3/m3-p1-4-compiled-parts-benchmark.md b/.github/tickets/m3/m3-p1-4-compiled-parts-benchmark.md index ce601ca..510b21d 100644 --- a/.github/tickets/m3/m3-p1-4-compiled-parts-benchmark.md +++ b/.github/tickets/m3/m3-p1-4-compiled-parts-benchmark.md @@ -15,7 +15,7 @@ The table should give, per contract, seeds solved, evaluations, and neurons, syn The n1 x n2 runs should repeat the M1 benchmark settings (10 seeds per algorithm, 6000 evaluations, lexicase, 5 sampled runs) with `--hand-built off`, because only then is the change attributable to the library. If n1 x n2 is not solved, the report should say so and give the best fitness and the parts the best networks hold, because an honest negative is still the result. -Where: `SNP_Evolution/Evolution/Benchmarking/Benchmark.cs`, the `evolve-parts` and `benchmark` commands; results in RESEARCH.md "Evolved against hand-designed arithmetic" and README "Composing machines from parts". +Where: `Search/Benchmarking/Benchmark.cs`, the `evolve-parts` and `benchmark` commands; results in RESEARCH.md "Evolved against hand-designed arithmetic" and README "Composing machines from parts". Done when: - [ ] The per-contract table is in RESEARCH.md, seeds 1 to 5 diff --git a/.github/tickets/m3/m3-p2-1-part-programs.md b/.github/tickets/m3/m3-p2-1-part-programs.md index ff07f1d..bb5bf0c 100644 --- a/.github/tickets/m3/m3-p2-1-part-programs.md +++ b/.github/tickets/m3/m3-p2-1-part-programs.md @@ -21,7 +21,7 @@ A count out-port should add to its register rather than overwrite it, because tw A register read by a call should be emptied by it, because a count port consumes what it reads; a program that needs a value twice must fan it out first, as the network must. The step estimate should be marked as an estimate, because exact timing comes only from the lowered network. -Where: new `SNP_Evolution/Evolution/Programs/` (or beside `SNP_Evolution/Compilation/RegisterMachine.cs`), `SNP_Evolution/Evolution/Contracts/Specification.cs`, `SNP_Evolution/Evolution/Modules/ModuleLibrary.cs`. +Where: new `Compilation/PartPrograms/` (or beside `Compilation/RegisterMachine.cs`), `Specs/Contracts/Specification.cs`, `Specs/Parts/ModuleLibrary.cs`. Done when: - [ ] A hand-written part program for n1 x n2 from register, add, zero test, decrement and fan-out gives every multiply case in the interpreter diff --git a/.github/tickets/m3/m3-p2-2-lower-programs.md b/.github/tickets/m3/m3-p2-2-lower-programs.md index b3c0bca..eec4ace 100644 --- a/.github/tickets/m3/m3-p2-2-lower-programs.md +++ b/.github/tickets/m3/m3-p2-2-lower-programs.md @@ -20,7 +20,7 @@ The lowering should only use parts from the library and plain relays, because a Where the lowering needs a part the library lacks (a join, a merge, a timer), it should say which, because that is a proposal the run can make. A disagreement between the interpreter and the network should be reported with the case and both readings, because it means either a lowering bug or a part whose contract does not say enough about timing. -Where: `SNP_Evolution/Evolution/Modules/Composition.cs`, `SNP_Evolution/Evolution/Modules/PartWiring.cs`, `SNP_Evolution/Evolution/Modules/Promotion.cs`; the n1 x n2 and Fibonacci programs from the previous ticket as tests. +Where: `Specs/Parts/Composition.cs`, `Specs/Parts/PartWiring.cs`, `Search/Modules/Promotion.cs`; the n1 x n2 and Fibonacci programs from the previous ticket as tests. Done when: - [ ] The hand-written n1 x n2 program lowers to a network that passes the multiply contract on the exhaustive engine and is promoted diff --git a/.github/tickets/m3/m3-p2-3-part-program-search.md b/.github/tickets/m3/m3-p2-3-part-program-search.md index eaf0850..2f7d17e 100644 --- a/.github/tickets/m3/m3-p2-3-part-program-search.md +++ b/.github/tickets/m3/m3-p2-3-part-program-search.md @@ -21,7 +21,7 @@ Its responsibilities are: Evaluations should be counted in the same units as composition search (one interpreter run per case counts as one evaluation of a network), plus every lowered network verified, because the two genomes must be compared on one budget. A program that solves in the interpreter but fails after lowering should be kept out of the population and logged, because the interpreter would otherwise reward it forever. -Where: new search beside the part-program interpreter; `SNP_Evolution/Evolution/Modules/CompositionSearch.cs` for the edit and evaluation conventions, `SNP_Evolution/Evolution/Proposals/PartProposals.cs`, `SNP_Evolution/Cli/CommandLine.cs` (`compose`), `SNP_Evolution/Evolution/Benchmarking/Benchmark.cs`. +Where: new search beside the part-program interpreter; `Search/EvolutionSearch.cs` (`CompositionSearch`) for the edit and evaluation conventions, `Search/Proposals/PartProposals.cs`, `Cli/Commands/ComposeCommand.cs`, `Search/Benchmarking/Benchmark.cs`. Done when: - [ ] `compose --task "Contract multiply" --genome program` solves from a library with no hand-built parts on at least half of 10 seeds, with the median evaluations reported next to composition search diff --git a/.github/tickets/m3/m3-p2-5-arithmetic-depth.md b/.github/tickets/m3/m3-p2-5-arithmetic-depth.md index 959484d..fcad932 100644 --- a/.github/tickets/m3/m3-p2-5-arithmetic-depth.md +++ b/.github/tickets/m3/m3-p2-5-arithmetic-depth.md @@ -16,7 +16,7 @@ Reuse should be reported per level: which promoted parts appear in each solution An n^k contract should be added to `ArithmeticParts` with a specification, keeping its cases small enough for the exhaustive engine, because it has to be verifiable to count. Sizes and steps should be added to the published-circuit table, because Zeng et al. give hand-built sizes for the same four operations. -Where: `SNP_Evolution/Evolution/Contracts/ArithmeticParts.cs`, `SNP_Evolution/Evolution/Contracts/Specification.cs`, `SNP_Evolution/Evolution/Modules/PartReuse.cs`; results in RESEARCH.md "Evolved against hand-designed arithmetic". +Where: `Specs/Contracts/ArithmeticParts.cs`, `Specs/Contracts/Specification.cs`, `Specs/Parts/PartReuse.cs`; results in RESEARCH.md "Evolved against hand-designed arithmetic". Done when: - [ ] Solved counts over 5 seeds for subtraction, division, comparison and n^k, by part-program and by composition search, are in RESEARCH.md diff --git a/.github/tickets/m3/m3-p3-1-counterexample-guided-search.md b/.github/tickets/m3/m3-p3-1-counterexample-guided-search.md index 26ef140..ff9818f 100644 --- a/.github/tickets/m3/m3-p3-1-counterexample-guided-search.md +++ b/.github/tickets/m3/m3-p3-1-counterexample-guided-search.md @@ -21,7 +21,7 @@ The loop should stop after a set number of counterexamples (8 by default) and re The counterexample should be the structured one the M2.5 verifier returns, because a counterexample as text cannot be scored. A counterexample should be added as a full case from the specification, so lexicase sees it as one more case, because a special penalty would be one more scoring rule to tune. -Where: `SNP_Evolution/Evolution/Contracts/BoundedCheck.cs` (`Admit`), `SNP_Evolution/Evolution/Contracts/PartEvolution.cs`, `SNP_Evolution/Evolution/Tasks/ContractTask.cs`, `SNP_Evolution/Evolution/Proposals/PartProposals.cs`, `SNP_Evolution/Evolution/Modules/Promotion.cs`, `SNP_Evolution/Storage/PartLibraryFiles.cs`. +Where: `Specs/Verification/BoundedCheck.cs` (`Admit`), `Search/PartSearch.cs`, `Specs/Tasks/ContractTask.cs`, `Search/Proposals/PartProposals.cs`, `Search/Modules/Promotion.cs`, `Storage/PartLibraryFiles.cs`. Done when: - [ ] Given the add loop as it was before its done waited for the accumulator, composition search with the counterexample loop on finds a version that passes admission past a = 14 diff --git a/.github/tickets/m3/m3-p3-2-proof-by-composition.md b/.github/tickets/m3/m3-p3-2-proof-by-composition.md index d5386a4..ee7a6e3 100644 --- a/.github/tickets/m3/m3-p3-2-proof-by-composition.md +++ b/.github/tickets/m3/m3-p3-2-proof-by-composition.md @@ -21,7 +21,7 @@ The use check should be stated as what the contract's cases assume and nothing m The timing of a child should come from its specification's latency bound, and a composition that relies on exact timing between children should be refused with the reason, because start and done are what make composition timing-safe, and anything else is unproven. Where the flattened check and the proof by composition both run, they should agree, and a test should cover this, because the new proof is only trusted where it has been checked. -Where: `SNP_Evolution/Evolution/Contracts/BoundedCheck.cs`, `SNP_Evolution/Evolution/Contracts/Specification.cs`, `SNP_Evolution/Evolution/Modules/Promotion.cs` (recipes), the `verify` command; tests in `SNP_Evolution.Tests/Evolution/BoundedCheckTests.cs`. +Where: `Specs/Verification/BoundedCheck.cs`, `Specs/Contracts/Specification.cs`, `Search/Modules/Promotion.cs` (recipes), the `verify` command; tests in `SNP_Evolution.Tests/Specs/Verification/BoundedCheckTests.cs`. Done when: - [ ] The add loop and the promoted multiplier are proven by composition to at least five times their flattened bounds in the same time diff --git a/.github/tickets/m3/m3-p4-1-bit-serial-adder.md b/.github/tickets/m3/m3-p4-1-bit-serial-adder.md index e56d56c..132cbc5 100644 --- a/.github/tickets/m3/m3-p4-1-bit-serial-adder.md +++ b/.github/tickets/m3/m3-p4-1-bit-serial-adder.md @@ -16,7 +16,7 @@ The comparison should give neurons, synapses and steps against Aimone et al.'s 4 The contract should be a family over width, because the binary primitives of the verified library in M5 are built on it. If the profile run also solves it, the part should be exported to NIR and checked by co-simulation, because a bit-serial adder is the part that fits integrate-and-fire hardware. -Where: `SNP_Evolution/Evolution/Contracts/ArithmeticParts.cs`, `SNP_Evolution/Evolution/Contracts/Specification.cs`, `SNP_Evolution/Evolution/Contracts/PortEncoding.cs`, `SNP_Evolution/Evolution/Contracts/PartEvolution.cs`; the published-circuit table in RESEARCH.md. +Where: `Specs/Contracts/ArithmeticParts.cs`, `Specs/Contracts/Specification.cs`, `Specs/Contracts/PortEncoding.cs`, `Search/PartSearch.cs`; the published-circuit table in RESEARCH.md. Done when: - [ ] The binary add contracts and their specifications are in the catalogue diff --git a/.github/tickets/m3/m3-p4-2-part-families.md b/.github/tickets/m3/m3-p4-2-part-families.md index a03685c..74389ae 100644 --- a/.github/tickets/m3/m3-p4-2-part-families.md +++ b/.github/tickets/m3/m3-p4-2-part-families.md @@ -19,7 +19,7 @@ A built member should be verified and proven like any other part, because compos A built member should be replaced by an evolved one only when the evolved one is cheaper, because the library already keeps the cheaper of two parts that read the same. The family's members should be one entry per k in the library, under the family's name, because composition and part programs need a concrete part to wire. -Where: `SNP_Evolution/Evolution/Contracts/Contract.cs`, `SNP_Evolution/Evolution/Contracts/FirstParts.cs`, `SNP_Evolution/Evolution/Modules/Promotion.cs`, `SNP_Evolution/Evolution/Modules/ModuleLibrary.cs`, `SNP_Evolution/Storage/PartLibraryFiles.cs`. +Where: `Specs/Contracts/Contract.cs`, `Specs/Contracts/FirstParts.cs`, `Search/Modules/Promotion.cs`, `Specs/Parts/ModuleLibrary.cs`, `Storage/PartLibraryFiles.cs`. Done when: - [ ] Delay k and sequencer k are families, and members for k up to 8 are built from the library's members, verified and proven diff --git a/.github/tickets/m3/m3-p4-3-library-compression.md b/.github/tickets/m3/m3-p4-3-library-compression.md index ad6c66c..533bde8 100644 --- a/.github/tickets/m3/m3-p4-3-library-compression.md +++ b/.github/tickets/m3/m3-p4-3-library-compression.md @@ -16,7 +16,7 @@ It should count, for each candidate sub-composition, how many promoted parts hol A candidate should only count if it has a contract that can be read off its ports and checked, because a part without a contract cannot be verified or reused. It should end in a recommendation: build compression into promotion, or leave it, with the numbers behind it. -Where: `SNP_Evolution/Evolution/Modules/Promotion.cs` (recipes), `SNP_Evolution/Evolution/Modules/PartReuse.cs`, the libraries left by the M3 benchmarks; the finding in RESEARCH.md "Toward general synthesis". +Where: `Search/Modules/Promotion.cs` (recipes), `Specs/Parts/PartReuse.cs`, the libraries left by the M3 benchmarks; the finding in RESEARCH.md "Toward general synthesis". Done when: - [ ] RESEARCH.md lists the candidate sub-compositions found, how many promoted parts hold each, and the recipe size saved diff --git a/.github/tickets/m4/m4-p1-1-algorithm-spec.md b/.github/tickets/m4/m4-p1-1-algorithm-spec.md index ebdc4dd..c167129 100644 --- a/.github/tickets/m4/m4-p1-1-algorithm-spec.md +++ b/.github/tickets/m4/m4-p1-1-algorithm-spec.md @@ -23,7 +23,7 @@ Case generation should be seeded and deterministic, because two runs of one spec A spec built for a catalogue contract should agree with that contract's cases and its `Specification`, checked with `Specifications.Disagreements`, because the catalogue is what existing parts were verified against. Training cases should include the edges of the domain (zero, one, the largest training value), because off-by-one parts pass the middle and fail the edges. -Where: new `SNP_Evolution/Evolution/Specs/AlgorithmSpec.cs`; `SNP_Evolution/Evolution/Contracts/Specification.cs`, `SNP_Evolution/Evolution/Contracts/Contract.cs`, `SNP_Evolution/Evolution/Contracts/PortEncoding.cs`, `SNP_Evolution/Evolution/Contracts/ArithmeticParts.cs`, `SNP_Evolution/Evolution/Tasks/ContractTask.cs`; tests in `SNP_Evolution.Tests/Evolution/`. +Where: new `Specs/Contracts/AlgorithmSpec.cs`; `Specs/Contracts/Specification.cs`, `Specs/Contracts/Contract.cs`, `Specs/Contracts/PortEncoding.cs`, `Specs/Contracts/ArithmeticParts.cs`, `Specs/Tasks/ContractTask.cs`; tests in `SNP_Evolution.Tests/Specs/`. Done when: - [ ] Specs for add, multiply, compare and fan-out make contracts with no disagreements against the catalogue's diff --git a/.github/tickets/m4/m4-p1-2-recurrence-task.md b/.github/tickets/m4/m4-p1-2-recurrence-task.md index e870983..7d6c737 100644 --- a/.github/tickets/m4/m4-p1-2-recurrence-task.md +++ b/.github/tickets/m4/m4-p1-2-recurrence-task.md @@ -21,7 +21,7 @@ A gap after a mistake should still earn credit when it follows the rule from the Coefficients as inputs should be optional, because a fixed rule is the first test and the family g(k) = c1 g(k-1) + c2 g(k-2) is the stretch. The compiled recurrence network should be the reference that scores 1, because `RecurrenceCompiler` already makes a network that follows the rule for any start it is built with. -Where: new `SNP_Evolution/Evolution/Tasks/RecurrenceTask.cs`; `SNP_Evolution/Evolution/Tasks/SequenceTask.cs`, `SNP_Evolution/Evolution/Tasks/TriggeredSequenceTask.cs`, `SNP_Evolution/Compilation/Recurrence.cs`, `SNP_Evolution/Compilation/RecurrenceCompiler.cs`, `SNP_Evolution/Evolution/Tasks/TaskSuite.cs`; the spec type from this epic. +Where: new `Specs/Tasks/RecurrenceTask.cs`; `Specs/Tasks/SequenceTask.cs`, `Specs/Tasks/TriggeredSequenceTask.cs`, `Compilation/Recurrence.cs`, `Compilation/RecurrenceCompiler.cs`, `Search/Benchmarking/TaskSuite.cs`; the spec type from this epic. Done when: - [ ] A network that lists the 16 Fibonacci gaps from 1,1 scores below 0.5 on the held-out cases diff --git a/.github/tickets/m4/m4-p1-3-relation-specs.md b/.github/tickets/m4/m4-p1-3-relation-specs.md index 42e4494..5c59f92 100644 --- a/.github/tickets/m4/m4-p1-3-relation-specs.md +++ b/.github/tickets/m4/m4-p1-3-relation-specs.md @@ -20,7 +20,7 @@ Every computation should have to satisfy the checker, not just one, because a pa A spec with no solution for an input should say so in its domain, and the part should answer on its no-answer done port, because a part that never fires done cannot be composed. The checker should not be used to make expected values for the case text, because two parts that give different valid answers read differently but are both right. -Where: the spec type from this epic; `SNP_Evolution/Evolution/Tasks/ContractTask.cs`, `SNP_Evolution/Evolution/Contracts/BoundedCheck.cs`, `SNP_Evolution/Evolution/Contracts/Specification.cs`, `SNP_Evolution/Simulation/ExhaustiveCpuEngine.cs`. +Where: the spec type from this epic; `Specs/Tasks/ContractTask.cs`, `Specs/Verification/BoundedCheck.cs`, `Specs/Contracts/Specification.cs`, `Simulation/ExhaustiveCpuEngine.cs`. Done when: - [ ] A "some divisor of n other than 1 and n, or no answer" spec scores a hand-built nondeterministic network as solved when every computation gives a valid divisor diff --git a/.github/tickets/m4/m4-p1-4-stream-specs.md b/.github/tickets/m4/m4-p1-4-stream-specs.md index f95d7b9..0103e84 100644 --- a/.github/tickets/m4/m4-p1-4-stream-specs.md +++ b/.github/tickets/m4/m4-p1-4-stream-specs.md @@ -18,7 +18,7 @@ Its responsibilities are: The rewritten debouncer and rate detector should give exactly the windows they give now for the same seed, because saved runs and benchmarks were scored on them. Held-out trains should run longer than training trains, because a controller that drifts or fills up shows it only after many windows. -Where: `SNP_Evolution/Evolution/Tasks/StreamingTask.cs`, `SNP_Evolution/Evolution/Tasks/SpikeTrains.cs`, `SNP_Evolution/Evolution/Tasks/TaskSuite.cs`; the spec type from this epic; tests in `SNP_Evolution.Tests/Evolution/StreamingTaskTests.cs`. +Where: `Specs/Tasks/StreamingTask.cs`, `Specs/Tasks/SpikeTrains.cs`, `Search/Benchmarking/TaskSuite.cs`; the spec type from this epic; tests in `SNP_Evolution.Tests/Specs/Tasks/StreamingTaskTests.cs`. Done when: - [ ] Debouncer and rate detector specs give the same windows as the current factory methods for seeds 1 to 4 diff --git a/.github/tickets/m4/m4-p2-1-verification-steps.md b/.github/tickets/m4/m4-p2-1-verification-steps.md index 6993197..868be62 100644 --- a/.github/tickets/m4/m4-p2-1-verification-steps.md +++ b/.github/tickets/m4/m4-p2-1-verification-steps.md @@ -21,7 +21,7 @@ A held-out failure should become a counterexample case like a proof's, because b Only candidates that pass training should go on to later steps, because held-out runs and proofs cost far more and most candidates fail training. The step reached should be part of the saved result, because a network that passed held-out cases but was never proven must not be reported as proven. -Where: new `SNP_Evolution/Evolution/Specs/SpecVerifier.cs`; `SNP_Evolution/Evolution/Contracts/BoundedCheck.cs`, `SNP_Evolution/Evolution/Contracts/PartEvolution.cs`, `SNP_Evolution/Storage/PartLibraryFiles.cs`, `SNP_Evolution/Cli/VerifyCommand.cs`. +Where: new `Specs/Verification/SpecVerifier.cs`; `Specs/Verification/BoundedCheck.cs`, `Search/PartSearch.cs`, `Storage/PartLibraryFiles.cs`, `Cli/Commands/VerifyCommand.cs`. Done when: - [ ] The add loop as it was before its done waited for the accumulator fails at the held-out or proof step with a counterexample at a = 14 or below, and the counterexample is added as a case diff --git a/.github/tickets/m4/m4-p2-2-superopt.md b/.github/tickets/m4/m4-p2-2-superopt.md index 42e40bf..a4e277b 100644 --- a/.github/tickets/m4/m4-p2-2-superopt.md +++ b/.github/tickets/m4/m4-p2-2-superopt.md @@ -23,7 +23,7 @@ A network should enter the map only after passing the verifier's held-out step, Evaluations should be charged to the shared evaluation budget in the same units as the other searches, because the synthesis arm is compared on the same budget. Under the hardware profile, seeds that do not fit should be conformed first and verified again, because conforming can change behaviour. -Where: new `SNP_Evolution/Evolution/Specs/Superoptimiser.cs`; `SNP_Evolution/Compilation/ShrinkRun.cs`, `SNP_Evolution/Evolution/MapElites.cs`, `SNP_Evolution/Evolution/HardwareCost.cs`, `SNP_Evolution/Evolution/HardwareProfile.cs`, `SNP_Evolution/Evolution/EvaluationCounter.cs`, `SNP_Evolution/Compilation/RegisterMachineCompiler.cs`, `SNP_Evolution/Compilation/RecurrenceCompiler.cs`, `SNP_Evolution/Cli/CommandLine.cs`, `SNP_Evolution/Cli/CompileSession.cs`, `SNP_Evolution/Cli/ExportCommands.cs`. +Where: new `Search/Superoptimiser.cs`; `Search/ShrinkSearch.cs`, `Search/Algorithms/MapElites.cs`, `Specs/Parts/HardwareCost.cs`, `Model/HardwareProfile.cs`, `Specs/Accounting/EvaluationBudget.cs`, `Compilation/RegisterMachineCompiler.cs`, `Compilation/RecurrenceCompiler.cs`, `Cli/Commands/CommandRegistry.cs`, `Application/CompileService.cs`, `Cli/Commands/ExportCommands.cs`. Done when: - [ ] `superopt --spec add` and `superopt --spec multiply`, seeded from compiled networks, each return a network with fewer neurons than its seed that passes held-out cases and a bounded proof, on at least 8 of 10 seeds diff --git a/.github/tickets/m4/m4-p2-3-synthesis-arm.md b/.github/tickets/m4/m4-p2-3-synthesis-arm.md index ea36744..0fa4195 100644 --- a/.github/tickets/m4/m4-p2-3-synthesis-arm.md +++ b/.github/tickets/m4/m4-p2-3-synthesis-arm.md @@ -19,7 +19,7 @@ Its responsibilities are: Both arms should get the same evaluation budget counted the same way, because otherwise the comparison measures the budget. Synthesis should not be given the compiled seed in any form, including as a library part, because that would make it superoptimisation. -Where: `SNP_Evolution/Evolution/Benchmarking/Benchmark.cs`, `SNP_Evolution/Evolution/Benchmarking/Statistics.cs`, `SNP_Evolution/Evolution/Modules/CompositionSearch.cs`, the part-program search from M3 Part 2, the superoptimiser from this epic; results in RESEARCH.md. +Where: `Search/Benchmarking/Benchmark.cs`, `Specs/Accounting/Statistics.cs`, `Search/EvolutionSearch.cs` (`CompositionSearch`), the part-program search from M3 Part 2, the superoptimiser from this epic; results in RESEARCH.md. Done when: - [ ] Seeds 1 to 10 run in both modes on the add, multiply and Fibonacci recurrence specs, with solved counts, median evaluations, best cost and the training-only rate in RESEARCH.md diff --git a/.github/tickets/m4/m4-p3-1-spec-files.md b/.github/tickets/m4/m4-p3-1-spec-files.md index f9426d6..f35d2e8 100644 --- a/.github/tickets/m4/m4-p3-1-spec-files.md +++ b/.github/tickets/m4/m4-p3-1-spec-files.md @@ -21,7 +21,7 @@ Expressions should be limited to integer arithmetic, comparisons and conditional The file should be read into the C# spec type and nothing downstream should know it came from a file, because two paths to one spec would score differently. Built-in specs should be writable as files that give the same cases, because that is the test that the format covers what the code does. -Where: new `SNP_Evolution/Evolution/Specs/SpecFile.cs`; `SNP_Evolution/Cli/CommandLine.cs`, `SNP_Evolution/Cli/CommandOptions.cs`, `SNP_Evolution/Cli/InputParsing.cs`; the spec type from M4 Part 1; tests in `SNP_Evolution.Tests/Cli/`. +Where: new `Storage/SpecFiles.cs`; `Cli/Commands/CommandRegistry.cs`, `Cli/Commands/CommonOptions.cs`, `Cli/InputParsing.cs`; the spec type from M4 Part 1; tests in `SNP_Evolution.Tests/Cli/`. Done when: - [ ] Add, multiply, compare, the Fibonacci recurrence and the divisor relation are each written as a spec file that gives the same cases as the built-in spec diff --git a/.github/tickets/m4/m4-p3-2-result-bundle.md b/.github/tickets/m4/m4-p3-2-result-bundle.md index 5d1aa6a..3b8307a 100644 --- a/.github/tickets/m4/m4-p3-2-result-bundle.md +++ b/.github/tickets/m4/m4-p3-2-result-bundle.md @@ -19,7 +19,7 @@ Its responsibilities are: The report should state what was not checked as plainly as what was, because a user taking a part to hardware needs to know whether it was proven or only tested. Exports should only be written when their co-simulation passes, and the report should say why one is missing, because a Verilog file that disagrees with the network is worse than none. -Where: `SNP_Evolution/Cli/ExportCommands.cs`, `SNP_Evolution/Export/VerilogExporter.cs`, `SNP_Evolution/Export/VerilogTestbench.cs`, `SNP_Evolution/Export/NirExporter.cs`, `SNP_Evolution/Storage/NetworkFiles.cs`, the superoptimiser and verifier from M4 Part 2; `README.md`. +Where: `Cli/Commands/ExportCommands.cs`, `Export/VerilogExporter.cs`, `Export/VerilogTestbench.cs`, `Export/NirExporter.cs`, `Storage/NetworkFiles.cs`, the superoptimiser and verifier from M4 Part 2; `README.md`. Done when: - [ ] `superopt --spec add --profile --out ` writes a bundle whose Verilog and NIR co-simulate against the network, and whose report gives the bound proven diff --git a/.github/tickets/m4/m4-p4-1-arithmetic-records.md b/.github/tickets/m4/m4-p4-1-arithmetic-records.md index 7b57fd3..430fadc 100644 --- a/.github/tickets/m4/m4-p4-1-arithmetic-records.md +++ b/.github/tickets/m4/m4-p4-1-arithmetic-records.md @@ -21,7 +21,7 @@ Rows should be compared only within one encoding, because a unary circuit costs The report should say that our seeds come from our own compiler and not from the published designs, because the published circuits could not be opened and rebuilt. Binary rows should wait for the binary adder from M3 Part 4, because no binary contract has been solved yet. -Where: the specs from M4 Part 1, `superopt` from M4 Part 2, `SNP_Evolution/Evolution/Contracts/ArithmeticParts.cs`, `SNP_Evolution/Evolution/HardwareCost.cs`; the table in RESEARCH.md "Evolved against hand-designed arithmetic". +Where: the specs from M4 Part 1, `superopt` from M4 Part 2, `Specs/Contracts/ArithmeticParts.cs`, `Specs/Parts/HardwareCost.cs`; the table in RESEARCH.md "Evolved against hand-designed arithmetic". Done when: - [ ] Seeds 1 to 10 run per spec, with and without the profile, and the best verified network per spec is saved under `parts/` or `parts-profile/` diff --git a/.github/tickets/m4/m4-p4-2-recurrence-family.md b/.github/tickets/m4/m4-p4-2-recurrence-family.md index 8fe8fba..c20890f 100644 --- a/.github/tickets/m4/m4-p4-2-recurrence-family.md +++ b/.github/tickets/m4/m4-p4-2-recurrence-family.md @@ -19,7 +19,7 @@ Its responsibilities are: Fibonacci should be one row among the family and not the headline, because a method that only finds Fibonacci is a Fibonacci method. The family with coefficients as inputs should be reported even if neither mode solves it, with where each stops, because that is the "solve equations" case and its limit is the finding. -Where: `RecurrenceTask` and the generator spec from M4 Part 1, `superopt` and the synthesis arm from M4 Part 2, `SNP_Evolution/Compilation/RecurrenceCompiler.cs`; results in RESEARCH.md. +Where: `RecurrenceTask` and the generator spec from M4 Part 1, `superopt` and the synthesis arm from M4 Part 2, `Compilation/RecurrenceCompiler.cs`; results in RESEARCH.md. Done when: - [ ] Seeds 1 to 10 run in both modes for each recurrence, with solved counts, held-out pass rates and sizes in RESEARCH.md diff --git a/.github/tickets/m4/m4-p4-3-inductive-proofs.md b/.github/tickets/m4/m4-p4-3-inductive-proofs.md index 57d2c9c..483fc6b 100644 --- a/.github/tickets/m4/m4-p4-3-inductive-proofs.md +++ b/.github/tickets/m4/m4-p4-3-inductive-proofs.md @@ -22,7 +22,7 @@ The prototype should be checked against `BoundedCheck` where both run, because a The step encoding should be written so the SMT encoding in M5 can reuse it, because exact synthesis and induction need the same terms for one step. The external solver should be optional and its tests should skip when it is missing, as the Uppaal and iverilog tests do, because not every machine has it. -Where: `SNP_Evolution/Evolution/Contracts/BoundedCheck.cs`, `SNP_Evolution/Simulation/` (the lasso tables for rule conditions), `SNP_Evolution/Export/ExternalTool.cs`; Pérez-Jiménez et al. 2024 in RESEARCH.md "Proving contracts" for the by-hand method; results in RESEARCH.md. +Where: `Specs/Verification/BoundedCheck.cs`, `Simulation/` (the lasso tables for rule conditions), `Export/ExternalTool.cs`; Pérez-Jiménez et al. 2024 in RESEARCH.md "Proving contracts" for the by-hand method; results in RESEARCH.md. Done when: - [ ] RESEARCH.md says whether one round of the add part and of the Fibonacci round can be encoded and proven for every input, with solver times, or why not diff --git a/.github/tickets/m5/m5-p1-1-synapse-weights.md b/.github/tickets/m5/m5-p1-1-synapse-weights.md index 24e3325..68bb762 100644 --- a/.github/tickets/m5/m5-p1-1-synapse-weights.md +++ b/.github/tickets/m5/m5-p1-1-synapse-weights.md @@ -21,7 +21,7 @@ Weights should be non-negative unless a target allows inhibition, because standa Weights should be off by default in every search, because results so far were found without them and must stay reproducible. The step semantics should change in one place only, because each engine and exporter repeating the delivery rule is how they drift apart. -Where: `SNP_Evolution/Networks/` (synapse and network types), `SNP_Evolution/Simulation/NetworkSimulation.cs`, `SNP_Evolution/Simulation/ExhaustiveCpuEngine.cs`, `SNP_Evolution/Simulation/Metal/`, `SNP_Evolution/Export/VerilogExporter.cs`, `SNP_Evolution/Export/NirExporter.cs`, `SNP_Evolution/Export/UppaalDeclarations.cs`, `SNP_Evolution/Evolution/Operators/StructuralMutations.cs`, `SNP_Evolution/Storage/`. +Where: `Model/` (synapse and network types), `Simulation/NetworkSimulation.cs`, `Simulation/ExhaustiveCpuEngine.cs`, `Simulation/Metal/`, `Export/VerilogExporter.cs`, `Export/NirExporter.cs`, `Export/UppaalDeclarations.cs`, `Search/Operators/StructuralMutations.cs`, `Storage/`. Done when: - [ ] A network with weights gives identical traces on every engine and under Verilog and NIR co-simulation, over 25 random weighted networks diff --git a/.github/tickets/m5/m5-p1-2-targets.md b/.github/tickets/m5/m5-p1-2-targets.md index fe8100e..5d9e42d 100644 --- a/.github/tickets/m5/m5-p1-2-targets.md +++ b/.github/tickets/m5/m5-p1-2-targets.md @@ -21,7 +21,7 @@ Its responsibilities are: Each chip limit should be cited from the vendor's documentation or a paper, and a limit with no source should be left unset rather than guessed, because a wrong limit is worse than none. `--profile hardware` should mean `--target generic-if`, because there should be one notion of fitting hardware. -Where: `SNP_Evolution/Evolution/HardwareProfile.cs`, `SNP_Evolution/Evolution/HardwareCost.cs`, `SNP_Evolution/Cli/CommandLine.cs`, `SNP_Evolution/Cli/ExportCommands.cs`, `SNP_Evolution/Cli/VerifyCommand.cs`. +Where: `Model/HardwareProfile.cs`, `Specs/Parts/HardwareCost.cs`, `Cli/Commands/CommandRegistry.cs`, `Cli/Commands/ExportCommands.cs`, `Cli/Commands/VerifyCommand.cs`. Done when: - [ ] Each target lists its limits and sources with `targets` diff --git a/.github/tickets/m5/m5-p1-3-nir-conformance.md b/.github/tickets/m5/m5-p1-3-nir-conformance.md index c1aee6f..e69afb4 100644 --- a/.github/tickets/m5/m5-p1-3-nir-conformance.md +++ b/.github/tickets/m5/m5-p1-3-nir-conformance.md @@ -19,7 +19,7 @@ Its responsibilities are: The profile should be stated so another NIR backend can implement it without our code, because the point is that parts move between simulators and chips. Expected traces should come from our engine and be checked by co-simulation before they are written, because a conformance suite with a wrong answer fails correct backends. -Where: `SNP_Evolution/Export/NirExporter.cs`, `tools/snp_nir.py`, `SNP_Evolution/Cli/ExportCommands.cs`; the document in `docs/` or RESEARCH.md. +Where: `Export/NirExporter.cs`, `tools/snp_nir.py`, `Cli/Commands/ExportCommands.cs`; the document in a new `docs/` folder or RESEARCH.md. Done when: - [ ] The profile document exists and every rule in it is covered by at least one conformance case diff --git a/.github/tickets/m5/m5-p2-1-smt-encoding.md b/.github/tickets/m5/m5-p2-1-smt-encoding.md index fba856e..08c6a61 100644 --- a/.github/tickets/m5/m5-p2-1-smt-encoding.md +++ b/.github/tickets/m5/m5-p2-1-smt-encoding.md @@ -21,7 +21,7 @@ The step should be generated from the same semantics the engines use, not writte Encoded runs of fixed networks should be checked against the engine on every step, because the encoding is only trusted once it agrees. Rules outside the encodable fragment should be refused by name, because a silently wrong encoding would make every proof built on it wrong. -Where: new `SNP_Evolution/Export/SmtExporter.cs` or the search folder from M2.5; `SNP_Evolution/Networks/SpikeCondition.cs` (lasso tables), `SNP_Evolution/Export/ExternalTool.cs`, the step semantics from M2.5. If the inductive-proof investigation in M4 built an encoding, start from it. +Where: new `Export/SmtExporter.cs` or `Search/`; `Model/SpikeCondition.cs` (lasso tables), `Export/ExternalTool.cs`, the step semantics from M2.5. If the inductive-proof investigation in M4 built an encoding, start from it. Done when: - [ ] For the first parts and 25 random profile networks, Z3 finds the encoded run equal to the engine's trace on every step diff --git a/.github/tickets/m5/m5-p2-3-windowed-improvement.md b/.github/tickets/m5/m5-p2-3-windowed-improvement.md index c02cc6b..a231f63 100644 --- a/.github/tickets/m5/m5-p2-3-windowed-improvement.md +++ b/.github/tickets/m5/m5-p2-3-windowed-improvement.md @@ -19,7 +19,7 @@ Its responsibilities are: A replacement should be kept only when the whole network still passes the verifier, because a window can be correct on its observed traces and still break timing the rest of the network relies on. Windows should follow start and done boundaries where they exist, because those are the boundaries where timing is already contracted. -Where: the exact synthesis from this epic; `SNP_Evolution/Evolution/Modules/PartWiring.cs`, `SNP_Evolution/Evolution/Modules/Composition.cs`; the superoptimiser from M4. +Where: the exact synthesis from this epic; `Specs/Parts/PartWiring.cs`, `Specs/Parts/Composition.cs`; the superoptimiser from M4. Done when: - [ ] On the compiled add and multiply networks and the hand-built Fibonacci composition, windowed improvement returns a smaller verified network, with each replacement reported diff --git a/.github/tickets/m5/m5-p2-4-proven-rewrites.md b/.github/tickets/m5/m5-p2-4-proven-rewrites.md index 0268791..f163c21 100644 --- a/.github/tickets/m5/m5-p2-4-proven-rewrites.md +++ b/.github/tickets/m5/m5-p2-4-proven-rewrites.md @@ -20,10 +20,10 @@ Its responsibilities are: A rule should only apply where its conditions hold in the whole network, because a rewrite that ignores a side input changes behaviour. Results should still go through the verifier while the method is new, because the rules are only trusted once their results agree with it. -Where: `SNP_Evolution/Compilation/ShrinkRun.cs`, `SNP_Evolution/Simulation/ExhaustiveCpuEngine.cs`, `SNP_Evolution/Networks/`; results in RESEARCH.md. +Where: `Search/ShrinkSearch.cs`, `Simulation/ExhaustiveCpuEngine.cs`, `Model/`; results in RESEARCH.md. Done when: -- [ ] RESEARCH.md lists the rules found, their conditions, and the shrink they give on the compiled add, multiply and Fibonacci networks next to `ShrinkRun`'s +- [ ] RESEARCH.md lists the rules found, their conditions, and the shrink they give on the compiled add, multiply and Fibonacci networks next to `ShrinkSearch`'s - [ ] Every network shrunk by rules passes the verifier - [ ] It ends with a build-or-leave recommendation for e-graphs - [ ] `dotnet test` green diff --git a/.github/tickets/m5/m5-p2-5-enumerative-baseline.md b/.github/tickets/m5/m5-p2-5-enumerative-baseline.md index 37752c4..fee28fa 100644 --- a/.github/tickets/m5/m5-p2-5-enumerative-baseline.md +++ b/.github/tickets/m5/m5-p2-5-enumerative-baseline.md @@ -18,7 +18,7 @@ Its responsibilities are: The behaviour key should be the same one the library uses to find duplicate parts, because two definitions of "behaves the same" would disagree. The search should report how far it got when it runs out of budget, because the size it exhausted is a lower bound on the smallest network. -Where: the search interface from M2.5; `SNP_Evolution/Evolution/Modules/ModuleLibrary.cs` (behaviour dedupe), `SNP_Evolution/Evolution/EvaluationCounter.cs`. +Where: the search interface from M2.5; `Specs/Parts/ModuleLibrary.cs` (behaviour dedupe), `Specs/Accounting/EvaluationBudget.cs`. Done when: - [ ] The enumerative search solves delay 1 to 4 and reports the size it exhausted for join and bit-serial add within the default budget diff --git a/.github/tickets/m5/m5-p2-6-search-benchmark.md b/.github/tickets/m5/m5-p2-6-search-benchmark.md index 6515e17..b3fb04e 100644 --- a/.github/tickets/m5/m5-p2-6-search-benchmark.md +++ b/.github/tickets/m5/m5-p2-6-search-benchmark.md @@ -15,7 +15,7 @@ The table should give, per spec and method: solved seeds, evaluations and wall t Methods that are deterministic should be run once and marked so, because seeds mean nothing to them. Wall time should be reported beside evaluations, because one solver call and one simulation are not the same cost. -Where: `SNP_Evolution/Evolution/Benchmarking/Benchmark.cs`, `SNP_Evolution/Evolution/Benchmarking/Statistics.cs`; results in RESEARCH.md. +Where: `Search/Benchmarking/Benchmark.cs`, `Specs/Accounting/Statistics.cs`; results in RESEARCH.md. Done when: - [ ] The table is in RESEARCH.md for delay 1 to 4, join, bit-serial add, add and multiply diff --git a/.github/tickets/m5/m5-p3-1-timing-primitives.md b/.github/tickets/m5/m5-p3-1-timing-primitives.md index f6b9d57..ae32240 100644 --- a/.github/tickets/m5/m5-p3-1-timing-primitives.md +++ b/.github/tickets/m5/m5-p3-1-timing-primitives.md @@ -20,7 +20,7 @@ Ties should be specified, not left to the network, because a part that breaks ti Parameters (k, w, r, d) should use the part-family form, because one contract per value does not scale. The published size of each primitive, where a paper gives one, should be recorded beside the spec, because the comparison is the result. -Where: the spec type from M4; `SNP_Evolution/Evolution/Contracts/FirstParts.cs`, the part families from M3; RESEARCH.md "A verified spiking parts library, and search beyond evolution" for the published designs. +Where: the spec type from M4; `Specs/Contracts/FirstParts.cs`, the part families from M3; RESEARCH.md "A verified spiking parts library, and search beyond evolution" for the published designs. Done when: - [ ] Each primitive is a spec with ties stated, for k up to 4 and w, r, d up to 8 diff --git a/.github/tickets/m5/m5-p3-2-binary-primitives.md b/.github/tickets/m5/m5-p3-2-binary-primitives.md index 99c07da..4f166d9 100644 --- a/.github/tickets/m5/m5-p3-2-binary-primitives.md +++ b/.github/tickets/m5/m5-p3-2-binary-primitives.md @@ -19,7 +19,7 @@ Its responsibilities are: A part that only works at the widths it was found at should be reported as such, because a serial part should not depend on width beyond its carry, and one that does is a lookup table. Sizes should be compared with Aimone et al.'s streaming adder and Chen and Guo's binary circuits, because those are the published binary rows. -Where: the spec type from M4; the bit-serial adder from M3 Part 4; `SNP_Evolution/Evolution/Contracts/ArithmeticParts.cs`, `SNP_Evolution/Evolution/Contracts/PortEncoding.cs`. +Where: the spec type from M4; the bit-serial adder from M3 Part 4; `Specs/Contracts/ArithmeticParts.cs`, `Specs/Contracts/PortEncoding.cs`. Done when: - [ ] Each primitive has a verified network proven for every input at widths 1 to 8 under `generic-if` diff --git a/.github/tickets/m5/m5-p3-3-state-machines.md b/.github/tickets/m5/m5-p3-3-state-machines.md index 03ce661..f1a7c02 100644 --- a/.github/tickets/m5/m5-p3-3-state-machines.md +++ b/.github/tickets/m5/m5-p3-3-state-machines.md @@ -18,7 +18,7 @@ Its responsibilities are: Generated trains should cover every transition at least once in training and in held-out runs, because a transition never exercised is never checked. Simultaneous inputs should be stated in the table (a priority, or an error output), because otherwise the machine's behaviour on them is undefined. -Where: the stream spec from M4 Part 1 and the spec file format from M4 Part 3; `SNP_Evolution/Evolution/Tasks/StreamingTask.cs`. +Where: the stream spec from M4 Part 1 and the spec file format from M4 Part 3; `Specs/Tasks/StreamingTask.cs`. Done when: - [ ] The three examples are tables that make stream specs, and the debouncer's matches the existing debouncer task diff --git a/.github/tickets/m5/m5-p3-4-library-release.md b/.github/tickets/m5/m5-p3-4-library-release.md index e444fd5..ab5b3c8 100644 --- a/.github/tickets/m5/m5-p3-4-library-release.md +++ b/.github/tickets/m5/m5-p3-4-library-release.md @@ -20,7 +20,7 @@ Its responsibilities are: A release should be rebuilt and every part verified again before it is written, because a release is a promise about every part in it. The index should be generated, never edited by hand, because hand edits drift from the parts. -Where: the result bundle from M4 Part 3; `SNP_Evolution/Storage/PartLibraryFiles.cs`, `SNP_Evolution/Cli/ExportCommands.cs`. +Where: the result bundle from M4 Part 3; `Storage/PartLibraryFiles.cs`, `Cli/Commands/ExportCommands.cs`. Done when: - [ ] `library release` writes a folder whose index lists every part, and every entry's exports co-simulate diff --git a/.github/tickets/where.py b/.github/tickets/where.py new file mode 100644 index 0000000..013245d --- /dev/null +++ b/.github/tickets/where.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Checks that every path named in an open ticket's `Where:` line exists. + + python3 .github/tickets/where.py open issues, asked of GitHub with gh + python3 .github/tickets/where.py 46 112 only these issue numbers + +A path is a backticked token holding a slash or a file extension. It is looked up from the solution folder +`SNP_Evolution/` first, as the tickets README says, then from the repository root (for `tools/`, `README.md` and the +like). A path written after "new" is one the ticket creates, so only the folder it goes in must exist. +""" +import json +import pathlib +import re +import subprocess +import sys + +ROOT = pathlib.Path(__file__).resolve().parents[2] +SOLUTION = ROOT / "SNP_Evolution" +TICKETS = ROOT / ".github" / "tickets" + + +def open_issues(): + out = subprocess.run(["gh", "issue", "list", "--state", "open", "--limit", "500", "--json", "number"], + check=True, capture_output=True, text=True, cwd=ROOT).stdout + return {issue["number"] for issue in json.loads(out)} + + +def tickets(numbers): + for path in sorted(TICKETS.glob("*/*.md")): + text = path.read_text() + number = re.search(r"^number:\s*(\d+)\s*$", text, re.M) + if number and int(number.group(1)) in numbers: + yield int(number.group(1)), path, text + + +def paths(where): + for match in re.finditer(r"(\bnew\s+)?`([^`]+)`", where): + token = match.group(2) + if "/" in token or re.search(r"\.\w+$", token): + yield token, match.group(1) is not None + + +def exists(token, new): + # The app's old single-project folder would otherwise pass from the root. + bases = (SOLUTION,) if token.startswith("SNP_Evolution/") else (SOLUTION, ROOT) + for base in bases: + target = base / token.rstrip("/") + if (target.parent if new else target).exists(): + return True + return False + + +def main(): + numbers = {int(arg) for arg in sys.argv[1:]} or open_issues() + missing = 0 + checked = 0 + for number, path, text in tickets(numbers): + where = re.search(r"^Where:(.*)$", text, re.M) + if not where: + continue + for token, new in paths(where.group(1)): + checked += 1 + if not exists(token, new): + missing += 1 + print(f"#{number} {path.relative_to(ROOT)}: {'new ' if new else ''}{token}") + print(f"{checked} paths in {len(numbers)} tickets, {missing} missing") + return 1 if missing else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/README.md b/README.md index 8985bdf..fc6ec3f 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ Thanks to Newtonsoft for the JSON parser libraries which are provided with this From the `SNP_Evolution` folder: ``` -dotnet run --project SNP_Evolution # start the console menu +dotnet run --project Cli # start the console menu dotnet test # run the test suite dotnet test --filter "Speed!=slow" # the fast set, golden runs included, in seconds ``` -Warnings are errors, and `dotnet format --verify-no-changes` must pass; both projects share `Directory.Build.props` and `.editorconfig`. Tests that run searches, time-limited bounded checks or external tools are marked `[Slow]`. CI (`.github/workflows/ci.yml`) builds, checks formatting and runs the fast set, then the slow set, on every push and pull request, on Linux with iverilog; the Metal, Uppaal and norse tests skip there. +Warnings are errors, and `dotnet format --verify-no-changes` must pass; every project shares `Directory.Build.props` and `.editorconfig`. Tests that run searches, time-limited bounded checks or external tools are marked `[Slow]`. CI (`.github/workflows/ci.yml`) builds, checks formatting and runs the fast set, then the slow set, on every push and pull request, on Linux with iverilog; the Metal, Uppaal and norse tests skip there. The golden runs in `SNP_Evolution.Tests/Golden` run fixed-seed commands (evolve, evolve-parts, compile, compose, verify and the three exports) and every engine on the reference networks and parts, and compare their output with the files in `Golden/Expected` exactly, evaluation counts included. A change that alters one fails them. When a change is meant to alter results, rerun with `SNP_UPDATE_GOLDEN=1 dotnet test --filter Golden` and say in the pull request why the files changed. @@ -30,7 +30,7 @@ sh install-snp.sh Run the script again after changing the code to reinstall the latest build. It adds `~/.dotnet/tools` to your `PATH` in `~/.zshrc` if needed. Elsewhere, from the `SNP_Evolution` folder: ``` -dotnet pack SNP_Evolution -c Release +dotnet pack Cli -c Release dotnet tool install --global --add-source ./nupkg SNP_Evolution ``` @@ -115,7 +115,7 @@ The *Rule Form* setting (Legacy, Standard or Mixed) picks the form of the rules ## Tasks -A task says what a network should do (`Evolution/Tasks/ITask`): +A task says what a network should do (`Specs/Tasks/ITask`): - **Generator**: with no input, produce exactly a set of numbers, such as a set target from the settings. - **Sequence**: with no input, space the output spikes by the target's gaps, in order (`SequenceTask`). Each gap that is wrong but close earns partial credit. @@ -124,7 +124,7 @@ A task says what a network should do (`Evolution/Tasks/ITask`): The last two read the whole output spike train (`Readout.SpikeTrain`), not just the first two spikes. `OutputTarget` turns a typed target into the matching task. - **Function**: read numbers on the input neurons, each as two spikes n steps apart, and output f(n). Examples are `n + 1`, `2n` and `n1 + n2`. Wrong but close outputs earn partial credit. - **Acceptor**: read a number on the input neuron and halt if and only if it belongs to the set. Scored by balanced accuracy. -- **Contract**: behave as a part with a start trigger, one or more done triggers and typed data ports (interval, count, trigger or binary), as a `Contract` in `Evolution/Contracts/` describes (`ContractTask`). Each case is checked against four rules: nothing is sent before start, done fires exactly once with the right outputs, every neuron ends with the spikes it started with, and done fires within the maximum latency. It reads the named port neurons (`Readout.Ports`), which the exhaustive engine follows over every computation. `ReferenceParts` holds a hand-built delay and register that meet their contracts. +- **Contract**: behave as a part with a start trigger, one or more done triggers and typed data ports (interval, count, trigger or binary), as a `Contract` in `Specs/Contracts/` describes (`ContractTask`). Each case is checked against four rules: nothing is sent before start, done fires exactly once with the right outputs, every neuron ends with the spikes it started with, and done fires within the maximum latency. It reads the named port neurons (`Readout.Ports`), which the exhaustive engine follows over every computation. `ReferenceParts` holds a hand-built delay and register that meet their contracts. - **Streaming**: a controller with no start or done (`StreamingTask`). A long sensor train arrives on the one input neuron, drawn from a fixed seed per case so every network sees the same trains, and the output's spike train is judged window by window, one check per window. Scored by balanced accuracy over windows that want output and windows that want silence, so a silent network earns 0.5 and one that copies its input less; it counts as solved only at 1. The **debouncer** answers each burst of at least m spikes within w steps with exactly one spike, and glitches of fewer spikes with none. The **rate detector** fires at least once in every window of a fast stretch (at least m spikes per w steps) and never in a slow one; the first window after each change of rate is not judged. @@ -145,7 +145,7 @@ The last two read the whole output spike train (`Readout.SpikeTrain`), not just - **MAP-Elites**: keeps the best network for every size, which maps out how fitness trades against size. - **NEAT-style speciated**: groups similar structures into species that share fitness, so new structures get time to improve. -Structural mutation (`Evolution/Operators/StructuralMutations.cs`) is a weighted mix of small edits. It can: +Structural mutation (`Search/Operators/StructuralMutations.cs`) is a weighted mix of small edits. It can: - nudge or replace an expression; - change a delay, a consumed or produced count, or whether a rule fires; @@ -337,12 +337,12 @@ The `evolve-parts` summary reports every library part's robustness at j = 1 and Both also run without the menu: ``` -dotnet run --project SNP_Evolution -c Release -- benchmark --budget 3000 --seeds 3 --task "Compute" --algorithm "MAP" -dotnet run --project SNP_Evolution -c Release -- select --task "Accept even" -dotnet run --project SNP_Evolution -c Release -- evolve --target "1,1,2,3,5,8,13" --generations 4000 --population 100 -dotnet run --project SNP_Evolution -c Release -- evolve --kind binary --target 001001001001 -dotnet run --project SNP_Evolution -c Release -- tasks # list the suite -dotnet run --project SNP_Evolution -c Release -- algorithms # list the searches +dotnet run --project Cli -c Release -- benchmark --budget 3000 --seeds 3 --task "Compute" --algorithm "MAP" +dotnet run --project Cli -c Release -- select --task "Accept even" +dotnet run --project Cli -c Release -- evolve --target "1,1,2,3,5,8,13" --generations 4000 --population 100 +dotnet run --project Cli -c Release -- evolve --kind binary --target 001001001001 +dotnet run --project Cli -c Release -- tasks # list the suite +dotnet run --project Cli -c Release -- algorithms # list the searches ``` `--task` and `--algorithm` match any name containing the text. These runs use the exhaustive engine unless given `--engine sampled`. `--lexicase on` picks parents by lexicase selection, and `--repetitions N` sets the sampled runs per network. A task or algorithm named exactly wins over those that only contain the name. Composition search builds from `--library DIR`, plus the hand-built parts with `--hand-built on`; a contract it solves is promoted into that run's library. @@ -355,36 +355,39 @@ dotnet run --project SNP_Evolution -c Release -- algorithms # list the searche snp-evolution reach --target "1,1,2,3,5,8,13,21,34" --evaluations 20000 --seeds 10 --setups flat,composition ``` -File Structure (under `SNP_Evolution/SNP_Evolution`): +The solution folder `SNP_Evolution/` holds one project per layer. They are listed lowest first, and each may reference only the projects listed before it. A project's namespace is `SnpEvolution.` followed by its folder name, and the tests in `SNP_Evolution.Tests/Layering` fail if a project references one listed after it or a namespace depends on itself. -- `Program.cs` starts the console menu, or runs a command when given arguments. -- `Networks/`: the SN P system. +- `Model/`: the SN P system. - `Rule`, `Neuron` and `Network` describe a system. - `SpikeCondition` compiles a rule expression into the exact set of spike counts it matches. - `NetworkNotation` renders a network as a readable text table. + - `HardwareProfile` says which rule forms and limits a chip takes. - `ReferenceNetworks` holds the hand-built natural and even numbers systems. - `Simulation/`: runs networks. - `CompiledNetwork` flattens a network into arrays. - `NetworkStep` steps one computation, with the rules chosen by its caller; `NetworkSimulation` chooses them at random. - `InputSpikes` describes what the environment feeds the input neurons. - `NetworkRunner` samples runs. - - The `ISimulationEngine` implementations run whole batches of trials, and declare what they support; `Metal/` holds the GPU engine. -- `Evolution/`: specifications, parts and verification, then the search. + - The `ISimulationEngine` implementations run whole batches of trials, and declare what they support; `Metal/` holds the GPU engine, whose kernel is embedded in this project. +- `Specs/`: what a network should do, and the checks that it does. + - `Accounting/` holds the `EvaluationBudget` every evaluation is charged to, what a search spent and why it stopped. - `Contracts/` holds contracts, their specifications and the catalogue built from them (the first parts and the arithmetic contracts). - `Tasks/` holds the tasks, their scoring and when a score counts as solved (`Solved`). - `Parts/` holds the part records, hand-built parts, compositions and recipes, and the module library. - `Verification/` holds the `Verifier`, the bounded check and the robustness measure. +- `Compilation/` compiles recurrences and register machines into networks. +- `Search/`: every search, behind `ISearch` and listed in `SearchCatalog`, with the run setup (`EvolutionContext`), composition search, part evolution, program search and the shrink. - `Genome/` makes random networks (`NetworkFactory`, `GenomeSpace`); `Fitness/` scores populations (`FitnessEvaluator`). - - `Operators/` holds the swappable mutation, crossover and selection operators; `Algorithms/` the genetic algorithms. + - `Operators/` holds the swappable mutation, crossover and selection operators; `Algorithms/` the genetic algorithms and the shared generation loop. - `Modules/` holds the modular loop: inserting and freezing modules, harvesting the changes that pay off, side runs on what is missing, promotion and the hand-built add loop. - - `Search/` holds the run setup (`EvolutionContext`, the algorithm catalog), composition search, part evolution and the shrink. - `Proposals/` proposes parts when a composition run stalls, from its failing checks or the shape of its target. - `Benchmarking/` holds the benchmark harness, the task suite and the algorithm selector. -- `Compilation/` compiles recurrences and register machines into networks. -- `Export/` writes Verilog, NIR and Uppaal models of networks. - `Storage/` saves and loads networks, parts and contracts as JSON, through one set of settings (`Json`), and fitness history as CSV. +- `Export/` writes Verilog, NIR and Uppaal models of networks and parts, and runs the external tools that check them. - `Application/` holds the settings and one run service per flow (evolve, compose, compile, evolve-parts, reach, benchmark, verify, export), which the menu, the command line and the tests all call. A service takes settings and a request, reports progress through a callback and returns a typed result; it never writes to the console. `Catalog` lists the engines, fitness functions, tasks and algorithms the settings offer, `PartLibraries` loads part libraries and part files, and `EvolutionSession` builds and runs one evolution. -- `Cli/` holds the console menus, one per submenu, and the command line. `Commands/` has one class per command, listed in `CommandRegistry`; `SettingOptions` declares each setting that is both a flag and a settings menu row once, for both. +- `Cli/` is the `snp-evolution` tool. `Program.cs` starts the console menu, or runs a command when given arguments. It holds the console menus, one per submenu, and the command line: `Commands/` has one class per command, listed in `CommandRegistry`, and `SettingOptions` declares each setting that is both a flag and a settings menu row once, for both. + +`SNP_Evolution.Tests/` mirrors these folders. The builders and setup more than one test needs (test networks, parts, temporary folders, console capture, engine theory data) are in `Fixtures/`, and `Golden/` holds the golden runs. ## How spikes are stored @@ -394,11 +397,13 @@ When an evolution finishes, the best network is printed and saved as a `.txt` ta ## Extending -Each swappable part is an interface plus one line in a catalog, after which it appears in the settings menu: +Each kind of extension is an interface to implement in the project it belongs to, plus one line where it is registered, after which the menus, the command line, the benchmark and the tests pick it up: -- **Simulation engine** (`ISimulationEngine`, listed in `Application/Catalog.cs`): receives a whole batch of trials (a network, its input and what to read back) so it can spread the runs out, and declares what it can run in `Support`. Read each network through `CompiledNetwork.Of(network)`, whose flat arrays are ready to copy to a GPU, and seed any per-run generators from the `Random` passed in, as `ParallelCpuEngine` does. -- **Fitness function** (`IFitnessFunction`, `Application/Catalog.cs`): scores a generator's sorted outputs from 0 to 1. -- **Task** (`ITask`, `Evolution/Benchmarking/TaskSuite.cs` or `Application/Catalog.cs`): lists its cases (input spikes and readout), then scores and describes the results. -- **Search** (`ISearch`, registered in `Evolution/Search/SearchCatalog.cs`): takes a `SearchRequest` (the task, the `EvaluationBudget` every evaluation is charged to, the run's random source and log, and optionally seeds, a stall policy and how to make and score networks) and returns a `SearchOutcome` (why it stopped, the best candidate, what it spent). A genetic algorithm only needs to implement `IGeneticAlgorithm`, built from the `EvolutionContext`, and be registered as an `EvolutionSearch`; it then runs in the shared generation loop with stall recovery and solve confirmation. Or combine new `IParentSelection`, `ICrossover` or `IMutation` operators with an existing algorithm. A network search that starts from nothing is automatically included in the benchmark and the selector; one that improves the networks it is given, such as the shrink, says `NeedsSeeds`. +- **Simulation engine**: implement `ISimulationEngine` in `Simulation/` and add it to `Catalog.Engines` in `Application/Catalog.cs`. An engine receives a whole batch of trials (a network, its input and what to read back) so it can spread the runs out, and declares what it can run in `Support`. Read each network through `CompiledNetwork.Of(network)`, whose flat arrays are ready to copy to a GPU, and seed any per-run generators from the `Random` passed in, as `ParallelCpuEngine` does. +- **Search**: implement `ISearch` in `Search/` and add it to `SearchCatalog.All` in `Search/SearchCatalog.cs` (or call `SearchCatalog.Register` at run time). A search takes a `SearchRequest` (the task, the `EvaluationBudget` every evaluation is charged to, the run's random source and log, and optionally seeds, a stall policy and how to make and score networks) and returns a `SearchOutcome` (why it stopped, the best candidate, what it spent). A genetic algorithm only needs to implement `IGeneticAlgorithm`, built from the `EvolutionContext`, and be registered as an `EvolutionSearch`; it then runs in the shared generation loop with stall recovery and solve confirmation. Or combine new `IParentSelection`, `ICrossover` or `IMutation` operators in `Search/Operators/` with an existing algorithm. A network search that starts from nothing is automatically included in the benchmark and the selector; one that improves the networks it is given, such as the shrink, says `NeedsSeeds`. +- **Task**: implement `ITask` in `Specs/Tasks/` and add it to `TaskSuite` in `Search/Benchmarking/TaskSuite.cs` for the benchmark, or to `Catalog.Tasks` in `Application/Catalog.cs` for the settings menu. A task lists its cases (input spikes and readout), then scores and describes the results. A part's behaviour is better written as a `Specification` in `Specs/Contracts/`, which the contract task, the verifier and the exporters all read. +- **Fitness function**: implement `IFitnessFunction` in `Specs/Tasks/` and add it to `Catalog.FitnessFunctions` in `Application/Catalog.cs`. It scores a generator's sorted outputs from 0 to 1. +- **Exporter**: exporters share no interface, since each target takes different inputs. Write the exporter in `Export/` as a static class that turns a network or a part into its files, add a method to `ExportService` in `Application/` that writes them and runs the target's own check, and give it a command as below, deriving from `ExportCommand` in `Cli/Commands/ExportCommands.cs`. +- **Command**: derive from `Command` in `Cli/Commands/` and add it to `CommandRegistry.All`. The command declares its name, a one-line summary and its options; usage, option parsing and errors come from the declarations, and a test checks the command table above against the registry. An option several commands take is declared once in `CommonOptions`, or in `SettingOptions` when it is also a settings menu row. The command should call a run service in `Application/` rather than run the flow itself. -A new command is one subclass of `Command` (in `Cli/Commands/`), which declares its name, a one-line summary and its options, plus one line in `CommandRegistry`. Usage, option parsing and errors come from the declarations, and a test checks the command table above against the registry. An option several commands take is declared once in `CommonOptions`, or in `SettingOptions` when it is also a settings menu row. The command should call a run service in `Application/` rather than run the flow itself. +New code goes in the lowest project that has everything it needs, and a project may only reference projects below it, so a check never depends on a search and a search never on the command line. diff --git a/SNP_Evolution/Application/Application.csproj b/SNP_Evolution/Application/Application.csproj new file mode 100644 index 0000000..d81c0cb --- /dev/null +++ b/SNP_Evolution/Application/Application.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/SNP_Evolution/SNP_Evolution/Application/BenchmarkService.cs b/SNP_Evolution/Application/BenchmarkService.cs similarity index 88% rename from SNP_Evolution/SNP_Evolution/Application/BenchmarkService.cs rename to SNP_Evolution/Application/BenchmarkService.cs index b09856e..e8ec8fd 100644 --- a/SNP_Evolution/SNP_Evolution/Application/BenchmarkService.cs +++ b/SNP_Evolution/Application/BenchmarkService.cs @@ -2,17 +2,17 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; namespace SnpEvolution.Application { // Benchmarks searches on suite tasks, or picks the best search for one task by successive halving. Both race the // searches that start from nothing; composition search among them builds from the settings' part library. - internal static class BenchmarkService + public static class BenchmarkService { // The benchmark settings, with the library's parts when a composition search is racing; Error when the library cannot be read. public static Loaded Plan(Settings settings, BenchmarkSettings benchmark, IReadOnlyList> searches) @@ -45,5 +45,5 @@ public static string Save(IReadOnlyList rows) } // The searches to race and the settings they race with. PartsNote says where composition search's parts come from. - internal sealed record BenchmarkPlan(IReadOnlyList> Searches, BenchmarkSettings Settings, string? PartsNote); + public sealed record BenchmarkPlan(IReadOnlyList> Searches, BenchmarkSettings Settings, string? PartsNote); } diff --git a/SNP_Evolution/SNP_Evolution/Application/Catalog.cs b/SNP_Evolution/Application/Catalog.cs similarity index 93% rename from SNP_Evolution/SNP_Evolution/Application/Catalog.cs rename to SNP_Evolution/Application/Catalog.cs index 21a3864..8db11c5 100644 --- a/SNP_Evolution/SNP_Evolution/Application/Catalog.cs +++ b/SNP_Evolution/Application/Catalog.cs @@ -1,20 +1,20 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; using SnpEvolution.Simulation; using SnpEvolution.Simulation.Metal; +using SnpEvolution.Specs.Tasks; namespace SnpEvolution.Application { - internal sealed record CatalogEntry(string Name, Func Create); + public sealed record CatalogEntry(string Name, Func Create); // The swappable parts offered in the settings menu. A new engine, fitness function or task only needs to implement // its interface and be listed here, and a new search to be registered in SearchCatalog; the first entry of each list // is the default. - internal static class Catalog + public static class Catalog { // The default: the GPU where there is one, which itself hands batches too small for it to the CPU. public static readonly CatalogEntry AutoEngine = MetalEngine.IsAvailable diff --git a/SNP_Evolution/SNP_Evolution/Application/CompileService.cs b/SNP_Evolution/Application/CompileService.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Application/CompileService.cs rename to SNP_Evolution/Application/CompileService.cs index 08326fa..4d61584 100644 --- a/SNP_Evolution/SNP_Evolution/Application/CompileService.cs +++ b/SNP_Evolution/Application/CompileService.cs @@ -3,13 +3,13 @@ using System.IO; using System.Linq; using SnpEvolution.Compilation; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; using SnpEvolution.Storage; namespace SnpEvolution.Application @@ -18,14 +18,14 @@ namespace SnpEvolution.Application // correct by construction, checks that it is, and evolves it smaller while it stays correct. A sequence of gaps // is fitted with a recurrence; a set is generated by a register program, evolved or read from a file. // ShrinkGenerations 0 only compiles; ProgramGenerations and ProgramLexicase are for evolving a register program. - internal sealed record CompileRequest(int ShrinkGenerations = CompileService.DefaultShrinkGenerations, string? ProgramFile = null, + public sealed record CompileRequest(int ShrinkGenerations = CompileService.DefaultShrinkGenerations, string? ProgramFile = null, int ProgramGenerations = CompileService.DefaultProgramGenerations, bool ProgramLexicase = true); // Compiled is null when the target could not be compiled, with the reason in the log; Solved says whether the // network kept, shrunk or not, solves the target. - internal sealed record CompileResult(Network? Compiled, bool Solved); + public sealed record CompileResult(Network? Compiled, bool Solved); - internal static class CompileService + public static class CompileService { public const int DefaultShrinkGenerations = 300; public const int DefaultProgramGenerations = 2000; diff --git a/SNP_Evolution/SNP_Evolution/Application/ComposeService.cs b/SNP_Evolution/Application/ComposeService.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution/Application/ComposeService.cs rename to SNP_Evolution/Application/ComposeService.cs index 1532692..eb2ec31 100644 --- a/SNP_Evolution/SNP_Evolution/Application/ComposeService.cs +++ b/SNP_Evolution/Application/ComposeService.cs @@ -1,11 +1,11 @@ using System; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; namespace SnpEvolution.Application { // Composition search for one suite task: a contract it solves is promoted to a part and the library saved. - internal static class ComposeService + public static class ComposeService { public const string FileStem = "ComposedNet"; diff --git a/SNP_Evolution/SNP_Evolution/Application/EngineChoice.cs b/SNP_Evolution/Application/EngineChoice.cs similarity index 82% rename from SNP_Evolution/SNP_Evolution/Application/EngineChoice.cs rename to SNP_Evolution/Application/EngineChoice.cs index 3b414ab..2a37c11 100644 --- a/SNP_Evolution/SNP_Evolution/Application/EngineChoice.cs +++ b/SNP_Evolution/Application/EngineChoice.cs @@ -5,7 +5,7 @@ namespace SnpEvolution.Application { // The engine benchmarks and evolve-parts run on: exhaustive unless sampled, which follows one random choice per run. // MaxConfigurations caps how many configurations the exhaustive engine follows at once. - internal sealed record EngineChoice(bool Sampled = false, int MaxConfigurations = ExhaustiveCpuEngine.DefaultMaxConfigurations) + public sealed record EngineChoice(bool Sampled = false, int MaxConfigurations = ExhaustiveCpuEngine.DefaultMaxConfigurations) { public Func Factory => Sampled ? () => new SequentialCpuEngine() : () => new ExhaustiveCpuEngine(MaxConfigurations); diff --git a/SNP_Evolution/SNP_Evolution/Application/EvolutionSession.cs b/SNP_Evolution/Application/EvolutionSession.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Application/EvolutionSession.cs rename to SNP_Evolution/Application/EvolutionSession.cs index eed6eca..2676491 100644 --- a/SNP_Evolution/SNP_Evolution/Application/EvolutionSession.cs +++ b/SNP_Evolution/Application/EvolutionSession.cs @@ -3,27 +3,27 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Proposals; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; +using SnpEvolution.Search.Proposals; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; using SnpEvolution.Storage; namespace SnpEvolution.Application { // Builds one evolution run from the settings, with the stages, modules, proposals and stagnation recovery they ask // for, and runs it to the end; the run services call it. - internal static class EvolutionSession + public static class EvolutionSession { // Whether this run evolves the target a few values at a time, and if so for what. public static bool IsIterative(Settings settings, BenchmarkTask task, [NotNullWhen(true)] out IPrefixTask? prefixTask) diff --git a/SNP_Evolution/SNP_Evolution/Application/EvolveService.cs b/SNP_Evolution/Application/EvolveService.cs similarity index 83% rename from SNP_Evolution/SNP_Evolution/Application/EvolveService.cs rename to SNP_Evolution/Application/EvolveService.cs index 5e616f7..77ca97f 100644 --- a/SNP_Evolution/SNP_Evolution/Application/EvolveService.cs +++ b/SNP_Evolution/Application/EvolveService.cs @@ -1,17 +1,17 @@ using System; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; namespace SnpEvolution.Application { // The network an evolution run starts from. - internal enum RunStart + public enum RunStart { Scratch, NaturalNumbers, @@ -19,10 +19,10 @@ internal enum RunStart } // Folder is where the files go, a new folder under runs/ unless given. - internal sealed record EvolveRequest(Settings Settings, Random Random, string FileStem, RunStart Start = RunStart.Scratch, string? Folder = null); + public sealed record EvolveRequest(Settings Settings, Random Random, string FileStem, RunStart Start = RunStart.Scratch, string? Folder = null); // Run is null, with Error saying why, when the run could not start. - internal sealed record EvolveResult(IGeneticAlgorithm? Run, bool Solved, string? Error = null) + public sealed record EvolveResult(IGeneticAlgorithm? Run, bool Solved, string? Error = null) { // How the run went, in a few words. public string Outcome @@ -39,7 +39,7 @@ public string Outcome // Evolves a network with the settings, saves the run's files and says whether it solved its task: the menu's // evolve and redo, and the evolve and compose commands. - internal static class EvolveService + public static class EvolveService { // The reference networks are generators, so they evolve towards the target whatever task is selected. public static BenchmarkTask TaskFor(Settings settings, RunStart start) => start == RunStart.Scratch diff --git a/SNP_Evolution/SNP_Evolution/Application/ExportService.cs b/SNP_Evolution/Application/ExportService.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Application/ExportService.cs rename to SNP_Evolution/Application/ExportService.cs index 62838a6..e489731 100644 --- a/SNP_Evolution/SNP_Evolution/Application/ExportService.cs +++ b/SNP_Evolution/Application/ExportService.cs @@ -3,21 +3,21 @@ using System.IO; using System.Linq; using System.Text; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; using SnpEvolution.Export; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; namespace SnpEvolution.Application { // A part or network to export. Cases are a part's contract cases, or one run with no input for a network. - internal sealed record ExportSource(string Name, Network Network, Part? Part, IReadOnlyList Ports, + public sealed record ExportSource(string Name, Network Network, Part? Part, IReadOnlyList Ports, IReadOnlyList<(string Label, InputSpikes Input, int Steps)> Cases); - internal enum ExportStatus + public enum ExportStatus { Written, Failed, @@ -25,11 +25,11 @@ internal enum ExportStatus } // Report is what was written and checked; Error says why the export failed or how the check differed. - internal sealed record ExportResult(ExportStatus Status, string Report, string? Error = null); + public sealed record ExportResult(ExportStatus Status, string Report, string? Error = null); // Writes a network as Verilog, NIR or Uppaal, and checks the file against our engine with the outside tool when it // is installed. - internal static class ExportService + public static class ExportService { public static Loaded Part(string file) => PartLibraries.ReadPart(file).Select(loaded => { diff --git a/SNP_Evolution/SNP_Evolution/Application/Loaded.cs b/SNP_Evolution/Application/Loaded.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution/Application/Loaded.cs rename to SNP_Evolution/Application/Loaded.cs index d7dd2c0..c7ad92a 100644 --- a/SNP_Evolution/SNP_Evolution/Application/Loaded.cs +++ b/SNP_Evolution/Application/Loaded.cs @@ -4,7 +4,7 @@ namespace SnpEvolution.Application { // A value read from files, or why it could not be read, so callers report the reason rather than catch it. - internal sealed record Loaded(T? Value, string? Error) where T : class + public sealed record Loaded(T? Value, string? Error) where T : class { public static Loaded Of(T value) => new Loaded(value, null); diff --git a/SNP_Evolution/SNP_Evolution/Application/NetworkRunService.cs b/SNP_Evolution/Application/NetworkRunService.cs similarity index 85% rename from SNP_Evolution/SNP_Evolution/Application/NetworkRunService.cs rename to SNP_Evolution/Application/NetworkRunService.cs index 75b8ed0..5684d04 100644 --- a/SNP_Evolution/SNP_Evolution/Application/NetworkRunService.cs +++ b/SNP_Evolution/Application/NetworkRunService.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; namespace SnpEvolution.Application { // A network's outputs over the settings' runs. - internal abstract record NetworkRun(IReadOnlyList Outputs) + public abstract record NetworkRun(IReadOnlyList Outputs) { // A network with inputs, scored on the selected task. public sealed record Scored(IReadOnlyList Outputs, BenchmarkTask Task, FitnessResult Score) : NetworkRun(Outputs); @@ -21,7 +21,7 @@ public sealed record SpikeTrain(IReadOnlyList Outputs, IReadOnlyList S } // Runs one network with the settings' engine and simulation options, for the menu's Run a network. - internal static class NetworkRunService + public static class NetworkRunService { public static NetworkRun Run(Settings settings, Network network, Random random) { diff --git a/SNP_Evolution/SNP_Evolution/Application/OutputTarget.cs b/SNP_Evolution/Application/OutputTarget.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Application/OutputTarget.cs rename to SNP_Evolution/Application/OutputTarget.cs index c2d4cb5..64c745c 100644 --- a/SNP_Evolution/SNP_Evolution/Application/OutputTarget.cs +++ b/SNP_Evolution/Application/OutputTarget.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Specs.Tasks; namespace SnpEvolution.Application { diff --git a/SNP_Evolution/SNP_Evolution/Application/PartLibraries.cs b/SNP_Evolution/Application/PartLibraries.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Application/PartLibraries.cs rename to SNP_Evolution/Application/PartLibraries.cs index d681602..0356e66 100644 --- a/SNP_Evolution/SNP_Evolution/Application/PartLibraries.cs +++ b/SNP_Evolution/Application/PartLibraries.cs @@ -2,14 +2,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; +using SnpEvolution.Search.Modules; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; namespace SnpEvolution.Application { // Reading part libraries and part files for every run, and saving what a composition run added to its library. - internal static class PartLibraries + public static class PartLibraries { // The library a run with these settings builds from. public static Loaded Load(Settings settings, Action? log = null) => @@ -59,5 +59,5 @@ public static void SaveIfGrown(Settings settings, ModuleLibrary? parts, int part } // A library part and the file it is saved in. - internal sealed record PartFile(LibraryPart Part, string Path); + public sealed record PartFile(LibraryPart Part, string Path); } diff --git a/SNP_Evolution/SNP_Evolution/Application/PartsService.cs b/SNP_Evolution/Application/PartsService.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution/Application/PartsService.cs rename to SNP_Evolution/Application/PartsService.cs index c6da153..2443109 100644 --- a/SNP_Evolution/SNP_Evolution/Application/PartsService.cs +++ b/SNP_Evolution/Application/PartsService.cs @@ -4,21 +4,21 @@ using System.IO; using System.Linq; using System.Text; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Search; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; namespace SnpEvolution.Application { // Seed is the run's seed, which each contract's own seed comes from. - internal sealed record PartsRequest(IReadOnlyList Contracts, int Seed = RunSeed.Repeatable, bool Redo = false, EngineChoice? Engine = null, int RobustJitter = 0); + public sealed record PartsRequest(IReadOnlyList Contracts, int Seed = RunSeed.Repeatable, bool Redo = false, EngineChoice? Engine = null, int RobustJitter = 0); // Error says why nothing was evolved, when the library folder cannot be used. - internal sealed record PartsResult(IReadOnlyList Rows, string? Error = null) + public sealed record PartsResult(IReadOnlyList Rows, string? Error = null) { public bool AllSolved => Error == null && Rows.All(row => row.Part != null); } @@ -28,7 +28,7 @@ internal sealed record PartsResult(IReadOnlyList Rows, string? // the run's seed, so the same seed writes the same library whichever contracts are run together. The summary reports every part's // robustness to jitter. RobustJitter, when above 0, makes the shrink keep the most robust part at that jitter rather than the smallest. // The part budget, library folder and hardware profile come from the settings, for the menu and the command line alike. - internal static class PartsService + public static class PartsService { // Robust holds the part's robustness at each of Robustness.Reported, and is null without a part. public sealed record Row(string Contract, string Status, long? Evaluations, LibraryPart? Part, IReadOnlyList? Robust = null); diff --git a/SNP_Evolution/SNP_Evolution/Application/ReachService.cs b/SNP_Evolution/Application/ReachService.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Application/ReachService.cs rename to SNP_Evolution/Application/ReachService.cs index 4e42545..6eacea1 100644 --- a/SNP_Evolution/SNP_Evolution/Application/ReachService.cs +++ b/SNP_Evolution/Application/ReachService.cs @@ -7,19 +7,19 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; namespace SnpEvolution.Application { // Runs each setup on seeds 1..N with the same budget and compares how far into the target they get. A run's reach is // how many of the target's checks its best network gets right, in order, before the first it misses. - internal static class ReachService + public static class ReachService { public sealed record Setup(string Name, string Description, Action Apply); diff --git a/SNP_Evolution/SNP_Evolution/Application/RunAdvisor.cs b/SNP_Evolution/Application/RunAdvisor.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Application/RunAdvisor.cs rename to SNP_Evolution/Application/RunAdvisor.cs index b231c5b..1ec7561 100644 --- a/SNP_Evolution/SNP_Evolution/Application/RunAdvisor.cs +++ b/SNP_Evolution/Application/RunAdvisor.cs @@ -1,24 +1,24 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Specs.Tasks; namespace SnpEvolution.Application { // One change the advisor recommends, with why, and how to make it. - internal sealed record Suggestion(string Setting, string Current, string Suggested, string Reason, Action Apply); + public sealed record Suggestion(string Setting, string Current, string Suggested, string Reason, Action Apply); // What the advisor found: changes it recommends and notes that need no change, such as what a run will cost. - internal sealed record Advice(IReadOnlyList Suggestions, IReadOnlyList Notes); + public sealed record Advice(IReadOnlyList Suggestions, IReadOnlyList Notes); // Looks over the settings for the selected task before a run and suggests changes that are known to help: a // likely typo in the target, a population too small to search with, iterative evolution for long targets, // stagnation recovery, genome limits that can express the target, and a generation budget that fits the stages. // A quick pilot can also race the algorithms on an early stage of the target and pick the best one. - internal static class RunAdvisor + public static class RunAdvisor { public const int MinimumPopulation = 30; public const int SuggestedPopulation = 50; diff --git a/SNP_Evolution/SNP_Evolution/Application/RunFolders.cs b/SNP_Evolution/Application/RunFolders.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Application/RunFolders.cs rename to SNP_Evolution/Application/RunFolders.cs index 781a850..4a08f19 100644 --- a/SNP_Evolution/SNP_Evolution/Application/RunFolders.cs +++ b/SNP_Evolution/Application/RunFolders.cs @@ -5,7 +5,7 @@ namespace SnpEvolution.Application { // Where runs write their files: runs/ beside parts/ at the root of the repository the program runs in, or in the // working directory outside one, and ignored by git so runs never land in the source tree. - internal static class RunFolders + public static class RunFolders { private const string RunsFolder = "runs"; diff --git a/SNP_Evolution/SNP_Evolution/Application/RunLayers.cs b/SNP_Evolution/Application/RunLayers.cs similarity index 89% rename from SNP_Evolution/SNP_Evolution/Application/RunLayers.cs rename to SNP_Evolution/Application/RunLayers.cs index 798327c..20eaa83 100644 --- a/SNP_Evolution/SNP_Evolution/Application/RunLayers.cs +++ b/SNP_Evolution/Application/RunLayers.cs @@ -1,13 +1,13 @@ -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Proposals; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Proposals; +using SnpEvolution.Specs.Tasks; namespace SnpEvolution.Application { // A run is the algorithm wrapped in stages, modules, proposals and stagnation recovery; these find the layers inside. - internal static class RunLayers + public static class RunLayers { // True when the whole target was matched, rather than only an early stage of it. public static bool IsSolved(IGeneticAlgorithm geneticAlgorithm, ITask? task = null) => diff --git a/SNP_Evolution/SNP_Evolution/Application/RunNotes.cs b/SNP_Evolution/Application/RunNotes.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Application/RunNotes.cs rename to SNP_Evolution/Application/RunNotes.cs index 197f53c..6671dbd 100644 --- a/SNP_Evolution/SNP_Evolution/Application/RunNotes.cs +++ b/SNP_Evolution/Application/RunNotes.cs @@ -1,14 +1,14 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Tasks; namespace SnpEvolution.Application { // What the user should know before evolving for a task: limits of the target and what the settings change. - internal static class RunNotes + public static class RunNotes { public static IReadOnlyList For(Settings settings, BenchmarkTask task) { diff --git a/SNP_Evolution/SNP_Evolution/Application/RunOutput.cs b/SNP_Evolution/Application/RunOutput.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Application/RunOutput.cs rename to SNP_Evolution/Application/RunOutput.cs index 1d3e925..673f737 100644 --- a/SNP_Evolution/SNP_Evolution/Application/RunOutput.cs +++ b/SNP_Evolution/Application/RunOutput.cs @@ -3,20 +3,20 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Proposals; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Proposals; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; using static SnpEvolution.Application.RunLayers; namespace SnpEvolution.Application { // What a finished run writes to its output folder and reports to the log. - internal static class RunOutput + public static class RunOutput { // Saves the fitness history and the best network, and reports the best network and what the run spent. public static void Save(IGeneticAlgorithm geneticAlgorithm, string folder, string fileStem, Action log, BudgetReport? evaluations = null) diff --git a/SNP_Evolution/SNP_Evolution/Application/RunSeed.cs b/SNP_Evolution/Application/RunSeed.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution/Application/RunSeed.cs rename to SNP_Evolution/Application/RunSeed.cs index 41ce848..70c3756 100644 --- a/SNP_Evolution/SNP_Evolution/Application/RunSeed.cs +++ b/SNP_Evolution/Application/RunSeed.cs @@ -4,7 +4,7 @@ namespace SnpEvolution.Application { // A run's random source: a given seed makes the run repeatable, and without one every run differs. evolve-parts and // compose write files later runs build on, so they default to seed 1 and the same command writes the same files. - internal static class RunSeed + public static class RunSeed { public const int Repeatable = 1; diff --git a/SNP_Evolution/SNP_Evolution/Application/Settings.cs b/SNP_Evolution/Application/Settings.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Application/Settings.cs rename to SNP_Evolution/Application/Settings.cs index 948892d..798b03b 100644 --- a/SNP_Evolution/SNP_Evolution/Application/Settings.cs +++ b/SNP_Evolution/Application/Settings.cs @@ -1,17 +1,17 @@ using System.Collections.Generic; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Proposals; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Proposals; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; namespace SnpEvolution.Application { - internal sealed class Settings + public sealed class Settings { public const int Elitism = SearchCatalog.Elitism; public const int MaxSpikeGroupSize = 4; @@ -37,7 +37,7 @@ internal sealed class Settings public int MaxInitialSpikes { get; set; } = MaxSpikeGroupSize; public bool DuplicateNeurons { get; set; } - // Keep every rule to threshold-and-reset forms, so evolved networks export to NIR (see Evolution.HardwareProfile). + // Keep every rule to threshold-and-reset forms, so evolved networks export to NIR (see Model.HardwareProfile). public bool HardwareProfile { get; set; } // Evolve sequences and binary words a few values at a time. Zero start length or step means automatic. diff --git a/SNP_Evolution/SNP_Evolution/Application/TargetPatterns.cs b/SNP_Evolution/Application/TargetPatterns.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Application/TargetPatterns.cs rename to SNP_Evolution/Application/TargetPatterns.cs diff --git a/SNP_Evolution/SNP_Evolution/Application/VerifyService.cs b/SNP_Evolution/Application/VerifyService.cs similarity index 83% rename from SNP_Evolution/SNP_Evolution/Application/VerifyService.cs rename to SNP_Evolution/Application/VerifyService.cs index 0abbff9..c63b4b9 100644 --- a/SNP_Evolution/SNP_Evolution/Application/VerifyService.cs +++ b/SNP_Evolution/Application/VerifyService.cs @@ -2,19 +2,19 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; namespace SnpEvolution.Application { // One part's proof: the bound it holds to, and the counterexample when it fails. - internal sealed record VerifiedPart(string Contract, ProvenBound Proven, TimeSpan Elapsed, string? Counterexample); + public sealed record VerifiedPart(string Contract, ProvenBound Proven, TimeSpan Elapsed, string? Counterexample); // Proves each part's contract for every input up to a bound, raised until the time per part runs out, and records // the bound in the part's file. - internal static class VerifyService + public static class VerifyService { // Reports each part as it is proven, since a proof can take the whole time allowed. public static IReadOnlyList Run(IReadOnlyList parts, ProofLimits limits, Action proven) diff --git a/SNP_Evolution/SNP_Evolution/Cli/BenchmarkMenu.cs b/SNP_Evolution/Cli/BenchmarkMenu.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Cli/BenchmarkMenu.cs rename to SNP_Evolution/Cli/BenchmarkMenu.cs index df6e7bf..61e1a82 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/BenchmarkMenu.cs +++ b/SNP_Evolution/Cli/BenchmarkMenu.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Diagnostics; using SnpEvolution.Application; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/Cli/Cli.csproj b/SNP_Evolution/Cli/Cli.csproj new file mode 100644 index 0000000..25b4f71 --- /dev/null +++ b/SNP_Evolution/Cli/Cli.csproj @@ -0,0 +1,16 @@ + + + + Exe + 1.0.0 + true + snp-evolution + SNP_Evolution + $(MSBuildThisFileDirectory)../nupkg + + + + + + + diff --git a/SNP_Evolution/SNP_Evolution/Cli/CommandLine.cs b/SNP_Evolution/Cli/CommandLine.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/CommandLine.cs rename to SNP_Evolution/Cli/CommandLine.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/AdviseCommand.cs b/SNP_Evolution/Cli/Commands/AdviseCommand.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/AdviseCommand.cs rename to SNP_Evolution/Cli/Commands/AdviseCommand.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/BenchmarkArgs.cs b/SNP_Evolution/Cli/Commands/BenchmarkArgs.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/BenchmarkArgs.cs rename to SNP_Evolution/Cli/Commands/BenchmarkArgs.cs index c6b1c11..7d908d5 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/Commands/BenchmarkArgs.cs +++ b/SNP_Evolution/Cli/Commands/BenchmarkArgs.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Application; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; namespace SnpEvolution.Cli { @@ -99,7 +99,7 @@ public override ExitCode Run(CommandArgs args) Console.WriteLine("Best algorithm for {0}: {1}", tasks[0].Name, result.Winner.Name); if (result.BestFound is Individual best) { - Console.WriteLine("Best network found (fitness {0}, {1}):\n{2}", best.Fitness, best.Description, Networks.NetworkNotation.Format(best.Genes)); + Console.WriteLine("Best network found (fitness {0}, {1}):\n{2}", best.Fitness, best.Description, Model.NetworkNotation.Format(best.Genes)); } return ExitCode.Success; } diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/Command.cs b/SNP_Evolution/Cli/Commands/Command.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/Command.cs rename to SNP_Evolution/Cli/Commands/Command.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/CommandArgs.cs b/SNP_Evolution/Cli/Commands/CommandArgs.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/CommandArgs.cs rename to SNP_Evolution/Cli/Commands/CommandArgs.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/CommandRegistry.cs b/SNP_Evolution/Cli/Commands/CommandRegistry.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/CommandRegistry.cs rename to SNP_Evolution/Cli/Commands/CommandRegistry.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/CommonOptions.cs b/SNP_Evolution/Cli/Commands/CommonOptions.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/CommonOptions.cs rename to SNP_Evolution/Cli/Commands/CommonOptions.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/CompileCommand.cs b/SNP_Evolution/Cli/Commands/CompileCommand.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/CompileCommand.cs rename to SNP_Evolution/Cli/Commands/CompileCommand.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/ComposeCommand.cs b/SNP_Evolution/Cli/Commands/ComposeCommand.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/ComposeCommand.cs rename to SNP_Evolution/Cli/Commands/ComposeCommand.cs index 545d8f7..bc755a8 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/Commands/ComposeCommand.cs +++ b/SNP_Evolution/Cli/Commands/ComposeCommand.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Application; -using SnpEvolution.Evolution.Benchmarking; +using SnpEvolution.Search.Benchmarking; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/EvolveCommand.cs b/SNP_Evolution/Cli/Commands/EvolveCommand.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/EvolveCommand.cs rename to SNP_Evolution/Cli/Commands/EvolveCommand.cs index 59f7cec..927f6df 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/Commands/EvolveCommand.cs +++ b/SNP_Evolution/Cli/Commands/EvolveCommand.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Application; -using SnpEvolution.Evolution.Benchmarking; +using SnpEvolution.Search.Benchmarking; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/EvolvePartsCommand.cs b/SNP_Evolution/Cli/Commands/EvolvePartsCommand.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/EvolvePartsCommand.cs rename to SNP_Evolution/Cli/Commands/EvolvePartsCommand.cs index d094c24..f8b4139 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/Commands/EvolvePartsCommand.cs +++ b/SNP_Evolution/Cli/Commands/EvolvePartsCommand.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Application; -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Specs.Contracts; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/ExportCommands.cs b/SNP_Evolution/Cli/Commands/ExportCommands.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/ExportCommands.cs rename to SNP_Evolution/Cli/Commands/ExportCommands.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/ListCommands.cs b/SNP_Evolution/Cli/Commands/ListCommands.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/ListCommands.cs rename to SNP_Evolution/Cli/Commands/ListCommands.cs index aa67f6b..5cb1aad 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/Commands/ListCommands.cs +++ b/SNP_Evolution/Cli/Commands/ListCommands.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/Option.cs b/SNP_Evolution/Cli/Commands/Option.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/Option.cs rename to SNP_Evolution/Cli/Commands/Option.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/ReachCommand.cs b/SNP_Evolution/Cli/Commands/ReachCommand.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/ReachCommand.cs rename to SNP_Evolution/Cli/Commands/ReachCommand.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/TargetArgs.cs b/SNP_Evolution/Cli/Commands/TargetArgs.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/TargetArgs.cs rename to SNP_Evolution/Cli/Commands/TargetArgs.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/ValueKind.cs b/SNP_Evolution/Cli/Commands/ValueKind.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/ValueKind.cs rename to SNP_Evolution/Cli/Commands/ValueKind.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/Commands/VerifyCommand.cs b/SNP_Evolution/Cli/Commands/VerifyCommand.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Cli/Commands/VerifyCommand.cs rename to SNP_Evolution/Cli/Commands/VerifyCommand.cs index 146e3a5..77b2896 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/Commands/VerifyCommand.cs +++ b/SNP_Evolution/Cli/Commands/VerifyCommand.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Application; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Specs.Verification; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/SNP_Evolution/Cli/CompileMenu.cs b/SNP_Evolution/Cli/CompileMenu.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/CompileMenu.cs rename to SNP_Evolution/Cli/CompileMenu.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/ConsoleInput.cs b/SNP_Evolution/Cli/ConsoleInput.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/ConsoleInput.cs rename to SNP_Evolution/Cli/ConsoleInput.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/ConsoleUi.cs b/SNP_Evolution/Cli/ConsoleUi.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/ConsoleUi.cs rename to SNP_Evolution/Cli/ConsoleUi.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/EvolveMenu.cs b/SNP_Evolution/Cli/EvolveMenu.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Cli/EvolveMenu.cs rename to SNP_Evolution/Cli/EvolveMenu.cs index 00ce2a8..0495cdd 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/EvolveMenu.cs +++ b/SNP_Evolution/Cli/EvolveMenu.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using SnpEvolution.Application; -using SnpEvolution.Evolution.Benchmarking; +using SnpEvolution.Search.Benchmarking; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/SNP_Evolution/Cli/InputParsing.cs b/SNP_Evolution/Cli/InputParsing.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/InputParsing.cs rename to SNP_Evolution/Cli/InputParsing.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/MainMenu.cs b/SNP_Evolution/Cli/MainMenu.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/MainMenu.cs rename to SNP_Evolution/Cli/MainMenu.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/MenuPrompts.cs b/SNP_Evolution/Cli/MenuPrompts.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/MenuPrompts.cs rename to SNP_Evolution/Cli/MenuPrompts.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/PartsMenu.cs b/SNP_Evolution/Cli/PartsMenu.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Cli/PartsMenu.cs rename to SNP_Evolution/Cli/PartsMenu.cs index 83680f0..5887348 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/PartsMenu.cs +++ b/SNP_Evolution/Cli/PartsMenu.cs @@ -1,6 +1,6 @@ using System; using SnpEvolution.Application; -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Specs.Contracts; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/SNP_Evolution/Program.cs b/SNP_Evolution/Cli/Program.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Program.cs rename to SNP_Evolution/Cli/Program.cs index 801d59f..d616d3b 100644 --- a/SNP_Evolution/SNP_Evolution/Program.cs +++ b/SNP_Evolution/Cli/Program.cs @@ -1,7 +1,7 @@ using System; using SnpEvolution.Cli; -namespace SnpEvolution +namespace SnpEvolution.Cli { internal static class Program { diff --git a/SNP_Evolution/SNP_Evolution/Cli/RedoMenu.cs b/SNP_Evolution/Cli/RedoMenu.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/RedoMenu.cs rename to SNP_Evolution/Cli/RedoMenu.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/RunMenu.cs b/SNP_Evolution/Cli/RunMenu.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Cli/RunMenu.cs rename to SNP_Evolution/Cli/RunMenu.cs index 36d8435..c63cd8b 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/RunMenu.cs +++ b/SNP_Evolution/Cli/RunMenu.cs @@ -1,8 +1,8 @@ using System; using System.Diagnostics; using SnpEvolution.Application; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Tasks; using SnpEvolution.Storage; namespace SnpEvolution.Cli diff --git a/SNP_Evolution/SNP_Evolution/Cli/SavedRuns.cs b/SNP_Evolution/Cli/SavedRuns.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Cli/SavedRuns.cs rename to SNP_Evolution/Cli/SavedRuns.cs index cb9548a..2c75331 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/SavedRuns.cs +++ b/SNP_Evolution/Cli/SavedRuns.cs @@ -6,10 +6,10 @@ using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using SnpEvolution.Application; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Tasks; using SnpEvolution.Storage; namespace SnpEvolution.Cli diff --git a/SNP_Evolution/SNP_Evolution/Cli/SearchMenu.cs b/SNP_Evolution/Cli/SearchMenu.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/SearchMenu.cs rename to SNP_Evolution/Cli/SearchMenu.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/SettingOptions.cs b/SNP_Evolution/Cli/SettingOptions.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Cli/SettingOptions.cs rename to SNP_Evolution/Cli/SettingOptions.cs index acfc40b..a20b2c9 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/SettingOptions.cs +++ b/SNP_Evolution/Cli/SettingOptions.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Application; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; namespace SnpEvolution.Cli { diff --git a/SNP_Evolution/SNP_Evolution/Cli/SettingsMenu.cs b/SNP_Evolution/Cli/SettingsMenu.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Cli/SettingsMenu.cs rename to SNP_Evolution/Cli/SettingsMenu.cs index 8ee4b33..9bb86e4 100644 --- a/SNP_Evolution/SNP_Evolution/Cli/SettingsMenu.cs +++ b/SNP_Evolution/Cli/SettingsMenu.cs @@ -1,5 +1,5 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Benchmarking; +using SnpEvolution.Search.Benchmarking; using static SnpEvolution.Cli.MenuPrompts; namespace SnpEvolution.Cli diff --git a/SNP_Evolution/SNP_Evolution/Cli/SettingsPage.cs b/SNP_Evolution/Cli/SettingsPage.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/SettingsPage.cs rename to SNP_Evolution/Cli/SettingsPage.cs diff --git a/SNP_Evolution/SNP_Evolution/Cli/TargetMenu.cs b/SNP_Evolution/Cli/TargetMenu.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Cli/TargetMenu.cs rename to SNP_Evolution/Cli/TargetMenu.cs diff --git a/SNP_Evolution/Compilation/Compilation.csproj b/SNP_Evolution/Compilation/Compilation.csproj new file mode 100644 index 0000000..1350f7a --- /dev/null +++ b/SNP_Evolution/Compilation/Compilation.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SNP_Evolution/SNP_Evolution/Compilation/Recurrence.cs b/SNP_Evolution/Compilation/Recurrence.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Compilation/Recurrence.cs rename to SNP_Evolution/Compilation/Recurrence.cs diff --git a/SNP_Evolution/SNP_Evolution/Compilation/RecurrenceCompiler.cs b/SNP_Evolution/Compilation/RecurrenceCompiler.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Compilation/RecurrenceCompiler.cs rename to SNP_Evolution/Compilation/RecurrenceCompiler.cs index d333a5c..7729fd9 100644 --- a/SNP_Evolution/SNP_Evolution/Compilation/RecurrenceCompiler.cs +++ b/SNP_Evolution/Compilation/RecurrenceCompiler.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Compilation { diff --git a/SNP_Evolution/SNP_Evolution/Compilation/RegisterMachine.cs b/SNP_Evolution/Compilation/RegisterMachine.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Compilation/RegisterMachine.cs rename to SNP_Evolution/Compilation/RegisterMachine.cs diff --git a/SNP_Evolution/SNP_Evolution/Compilation/RegisterMachineCompiler.cs b/SNP_Evolution/Compilation/RegisterMachineCompiler.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Compilation/RegisterMachineCompiler.cs rename to SNP_Evolution/Compilation/RegisterMachineCompiler.cs index b2617da..ba3fb83 100644 --- a/SNP_Evolution/SNP_Evolution/Compilation/RegisterMachineCompiler.cs +++ b/SNP_Evolution/Compilation/RegisterMachineCompiler.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Compilation { diff --git a/SNP_Evolution/Directory.Build.props b/SNP_Evolution/Directory.Build.props index 72be5c2..a159723 100644 --- a/SNP_Evolution/Directory.Build.props +++ b/SNP_Evolution/Directory.Build.props @@ -1,12 +1,18 @@ - + net10.0 14.0 enable enable true + SnpEvolution.$(MSBuildProjectName) + SnpEvolution.$(MSBuildProjectName) + + + + diff --git a/SNP_Evolution/Export/Export.csproj b/SNP_Evolution/Export/Export.csproj new file mode 100644 index 0000000..697919b --- /dev/null +++ b/SNP_Evolution/Export/Export.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SNP_Evolution/SNP_Evolution/Export/ExternalTool.cs b/SNP_Evolution/Export/ExternalTool.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Export/ExternalTool.cs rename to SNP_Evolution/Export/ExternalTool.cs diff --git a/SNP_Evolution/SNP_Evolution/Export/NetworkPort.cs b/SNP_Evolution/Export/NetworkPort.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution/Export/NetworkPort.cs rename to SNP_Evolution/Export/NetworkPort.cs index 8f418be..1b35296 100644 --- a/SNP_Evolution/SNP_Evolution/Export/NetworkPort.cs +++ b/SNP_Evolution/Export/NetworkPort.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; namespace SnpEvolution.Export { diff --git a/SNP_Evolution/SNP_Evolution/Export/NirExporter.cs b/SNP_Evolution/Export/NirExporter.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Export/NirExporter.cs rename to SNP_Evolution/Export/NirExporter.cs index 84ab822..681d9ff 100644 --- a/SNP_Evolution/SNP_Evolution/Export/NirExporter.cs +++ b/SNP_Evolution/Export/NirExporter.cs @@ -3,11 +3,11 @@ using System.IO; using System.Linq; using Newtonsoft.Json; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; namespace SnpEvolution.Export diff --git a/SNP_Evolution/SNP_Evolution/Export/UppaalDeclarations.cs b/SNP_Evolution/Export/UppaalDeclarations.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Export/UppaalDeclarations.cs rename to SNP_Evolution/Export/UppaalDeclarations.cs index a7261ac..2705d0d 100644 --- a/SNP_Evolution/SNP_Evolution/Export/UppaalDeclarations.cs +++ b/SNP_Evolution/Export/UppaalDeclarations.cs @@ -2,11 +2,11 @@ using System.Globalization; using System.Linq; using System.Text; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; namespace SnpEvolution.Export { diff --git a/SNP_Evolution/SNP_Evolution/Export/UppaalExporter.cs b/SNP_Evolution/Export/UppaalExporter.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Export/UppaalExporter.cs rename to SNP_Evolution/Export/UppaalExporter.cs index 4522fc4..0ca3d78 100644 --- a/SNP_Evolution/SNP_Evolution/Export/UppaalExporter.cs +++ b/SNP_Evolution/Export/UppaalExporter.cs @@ -4,11 +4,11 @@ using System.Linq; using System.Security; using System.Text; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; namespace SnpEvolution.Export diff --git a/SNP_Evolution/SNP_Evolution/Export/VerilogExporter.cs b/SNP_Evolution/Export/VerilogExporter.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Export/VerilogExporter.cs rename to SNP_Evolution/Export/VerilogExporter.cs index b89b254..bba210d 100644 --- a/SNP_Evolution/SNP_Evolution/Export/VerilogExporter.cs +++ b/SNP_Evolution/Export/VerilogExporter.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; namespace SnpEvolution.Export { diff --git a/SNP_Evolution/SNP_Evolution/Export/VerilogNeuronModules.cs b/SNP_Evolution/Export/VerilogNeuronModules.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Export/VerilogNeuronModules.cs rename to SNP_Evolution/Export/VerilogNeuronModules.cs index b09c0e9..a0f4c66 100644 --- a/SNP_Evolution/SNP_Evolution/Export/VerilogNeuronModules.cs +++ b/SNP_Evolution/Export/VerilogNeuronModules.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; namespace SnpEvolution.Export diff --git a/SNP_Evolution/SNP_Evolution/Export/VerilogTestbench.cs b/SNP_Evolution/Export/VerilogTestbench.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Export/VerilogTestbench.cs rename to SNP_Evolution/Export/VerilogTestbench.cs index 2e3050a..b5d879d 100644 --- a/SNP_Evolution/SNP_Evolution/Export/VerilogTestbench.cs +++ b/SNP_Evolution/Export/VerilogTestbench.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Verification; namespace SnpEvolution.Export { diff --git a/SNP_Evolution/SNP_Evolution/Networks/DelayKind.cs b/SNP_Evolution/Model/DelayKind.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Networks/DelayKind.cs rename to SNP_Evolution/Model/DelayKind.cs index e3bec4a..b80d56d 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/DelayKind.cs +++ b/SNP_Evolution/Model/DelayKind.cs @@ -1,4 +1,4 @@ -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // What a rule's delay d does to its neuron, decided once by Rule for every engine and exporter. public enum DelayKind diff --git a/SNP_Evolution/SNP_Evolution/Networks/ExpressionParser.cs b/SNP_Evolution/Model/ExpressionParser.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Networks/ExpressionParser.cs rename to SNP_Evolution/Model/ExpressionParser.cs index fcd9f6a..f180336 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/ExpressionParser.cs +++ b/SNP_Evolution/Model/ExpressionParser.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // Recursive descent over the regex subset that can describe spike counts: literals, '.', groups, // alternation and the ?, *, +, {n}, {n,} and {n,m} quantifiers (optionally lazy, which matches the same counts). diff --git a/SNP_Evolution/SNP_Evolution/Networks/HardwareProfile.cs b/SNP_Evolution/Model/HardwareProfile.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Networks/HardwareProfile.cs rename to SNP_Evolution/Model/HardwareProfile.cs index 2b05525..c6fe98e 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/HardwareProfile.cs +++ b/SNP_Evolution/Model/HardwareProfile.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // The rules integrate-and-fire hardware runs exactly, a^{>=k} / a^* -> a firing or forgetting, so a network within them exports to NIR. public static class HardwareProfile diff --git a/SNP_Evolution/Model/Model.csproj b/SNP_Evolution/Model/Model.csproj new file mode 100644 index 0000000..3e03f86 --- /dev/null +++ b/SNP_Evolution/Model/Model.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SNP_Evolution/SNP_Evolution/Networks/Network.cs b/SNP_Evolution/Model/Network.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Networks/Network.cs rename to SNP_Evolution/Model/Network.cs index 1262ea5..655da67 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/Network.cs +++ b/SNP_Evolution/Model/Network.cs @@ -3,7 +3,7 @@ using System.Linq; using Newtonsoft.Json; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { public sealed class Network { diff --git a/SNP_Evolution/SNP_Evolution/Networks/NetworkEdits.cs b/SNP_Evolution/Model/NetworkEdits.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Networks/NetworkEdits.cs rename to SNP_Evolution/Model/NetworkEdits.cs index 74f22be..069fd77 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/NetworkEdits.cs +++ b/SNP_Evolution/Model/NetworkEdits.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // Structural edits that keep a network well formed: synapses stay in range, never loop back to their own // neuron, and input and output neurons are never removed. diff --git a/SNP_Evolution/SNP_Evolution/Networks/NetworkGraph.cs b/SNP_Evolution/Model/NetworkGraph.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Networks/NetworkGraph.cs rename to SNP_Evolution/Model/NetworkGraph.cs index 9350ef2..d8b97f8 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/NetworkGraph.cs +++ b/SNP_Evolution/Model/NetworkGraph.cs @@ -5,7 +5,7 @@ using System.Security; using System.Text; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // Draws a network as an SN P system diagram in standalone SVG: each neuron a rounded box holding its spikes and // rules, each synapse an arrow, and arrows to and from the environment on the output and input neurons. diff --git a/SNP_Evolution/SNP_Evolution/Networks/NetworkNotation.cs b/SNP_Evolution/Model/NetworkNotation.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Networks/NetworkNotation.cs rename to SNP_Evolution/Model/NetworkNotation.cs index 5b296ea..6b5c5f7 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/NetworkNotation.cs +++ b/SNP_Evolution/Model/NetworkNotation.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // Renders a network as a text table in SN P notation, with spike counts written back out as runs of 'a'. public static class NetworkNotation diff --git a/SNP_Evolution/SNP_Evolution/Networks/NetworkPage.cs b/SNP_Evolution/Model/NetworkPage.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Networks/NetworkPage.cs rename to SNP_Evolution/Model/NetworkPage.cs index 981d269..d50fad8 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/NetworkPage.cs +++ b/SNP_Evolution/Model/NetworkPage.cs @@ -2,7 +2,7 @@ using System.Net; using System.Text; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // Renders a network as a standalone HTML page for exploring in a browser. CSS lays the neurons out in the same // columns as NetworkGraph, and a short script draws the synapses over them from the boxes' real positions, using diff --git a/SNP_Evolution/SNP_Evolution/Networks/Neuron.cs b/SNP_Evolution/Model/Neuron.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Networks/Neuron.cs rename to SNP_Evolution/Model/Neuron.cs index a142ede..5bb13f3 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/Neuron.cs +++ b/SNP_Evolution/Model/Neuron.cs @@ -2,7 +2,7 @@ using System.Linq; using Newtonsoft.Json; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // Marks a neuron as part of a copy of a library module: which module, and which copy of it in the network. public sealed record ModuleTag(int Module, int Instance); diff --git a/SNP_Evolution/SNP_Evolution/Networks/PositionAutomaton.cs b/SNP_Evolution/Model/PositionAutomaton.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Networks/PositionAutomaton.cs rename to SNP_Evolution/Model/PositionAutomaton.cs index 253f5d2..fd13719 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/PositionAutomaton.cs +++ b/SNP_Evolution/Model/PositionAutomaton.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using static SnpEvolution.Networks.ExpressionNode; +using static SnpEvolution.Model.ExpressionNode; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // The Glushkov construction of a rule expression: every 'a' in it is a position, and reading one spike moves each // active position to those that may follow it. Each copy of a repeated body gets its own positions. diff --git a/SNP_Evolution/SNP_Evolution/Networks/ReferenceNetworks.cs b/SNP_Evolution/Model/ReferenceNetworks.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Networks/ReferenceNetworks.cs rename to SNP_Evolution/Model/ReferenceNetworks.cs index 5574261..8939060 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/ReferenceNetworks.cs +++ b/SNP_Evolution/Model/ReferenceNetworks.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { public static class ReferenceNetworks { diff --git a/SNP_Evolution/SNP_Evolution/Networks/Rule.cs b/SNP_Evolution/Model/Rule.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Networks/Rule.cs rename to SNP_Evolution/Model/Rule.cs index fc2aa79..7105278 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/Rule.cs +++ b/SNP_Evolution/Model/Rule.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // Two rule forms share this class. A legacy rule (Consume is null) fires when the expression matches and empties // the neuron, as in the original program. A standard rule E/a^c -> a^p;d fires when the expression matches and the diff --git a/SNP_Evolution/SNP_Evolution/Networks/SpikeCondition.cs b/SNP_Evolution/Model/SpikeCondition.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Networks/SpikeCondition.cs rename to SNP_Evolution/Model/SpikeCondition.cs index eab8a0a..ab10828 100644 --- a/SNP_Evolution/SNP_Evolution/Networks/SpikeCondition.cs +++ b/SNP_Evolution/Model/SpikeCondition.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace SnpEvolution.Networks +namespace SnpEvolution.Model { // The spike counts a rule expression accepts. Expressions are regular expressions over the single letter 'a', // and such sets are always eventually periodic, so the set is stored as a lasso: a lookup table of TailLength diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/BenchmarkServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/BenchmarkServiceTests.cs index 5c64bfd..539ff84 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/BenchmarkServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/BenchmarkServiceTests.cs @@ -1,6 +1,6 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; +using SnpEvolution.Search.Fitness; namespace SnpEvolution.Tests.Application { @@ -9,24 +9,18 @@ public class BenchmarkServiceTests [Fact] public void OnlyARacingCompositionSearchReadsThePartLibrary() { - string folder = Path.Combine(Path.GetTempPath(), "snp-benchmark-" + Guid.NewGuid().ToString("N")); + using var temp = new TempFolder("snp-benchmark"); + string folder = temp.Path; var settings = new Settings { PartLibraryFolder = folder }; ISearch[] composing = { Catalog.StructuralDefault, SearchCatalog.CompositionTournament }; Assert.Null(BenchmarkService.Plan(settings, settings.BenchmarkSettings, new[] { Catalog.StructuralDefault }).Value!.PartsNote); Assert.StartsWith("Composition search builds from 0 part(s)", BenchmarkService.Plan(settings, settings.BenchmarkSettings, composing).Value!.PartsNote); - Directory.CreateDirectory(folder); - try - { - File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); + temp.Made(); + File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); - Assert.Contains("delay-2.json", BenchmarkService.Plan(settings, settings.BenchmarkSettings, composing).Error); - } - finally - { - Directory.Delete(folder, recursive: true); - } + Assert.Contains("delay-2.json", BenchmarkService.Plan(settings, settings.BenchmarkSettings, composing).Error); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/CatalogTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/CatalogTests.cs index 3f06515..f12499a 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/CatalogTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/CatalogTests.cs @@ -1,4 +1,5 @@ using SnpEvolution.Application; +using SnpEvolution.Search; namespace SnpEvolution.Tests.Application { @@ -12,5 +13,11 @@ public void AnExactNameWinsOverNamesThatOnlyContainIt() Assert.Equal(new[] { "Contract multiply" }, Catalog.Matching(names, name => name, "contract MULTIPLY")); Assert.Equal(new[] { "Contract multiply 4-bit", "Contract multiply" }, Catalog.Matching(names, name => name, "multiply")); } + + [Fact] + public void TheMenuOffersEveryGeneticAlgorithmInTheCatalog() + { + Assert.Equal(SearchCatalog.Evolution, SnpEvolution.Application.Catalog.Algorithms); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/ComposeSessionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/ComposeSessionTests.cs index 6023f56..3e5c37d 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/ComposeSessionTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/ComposeSessionTests.cs @@ -1,32 +1,27 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; using SnpEvolution.Storage; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; namespace SnpEvolution.Tests.Application { public sealed class ComposeSessionTests : IDisposable { - private readonly string folder = Path.Combine(Path.GetTempPath(), "snp-compose-" + Guid.NewGuid().ToString("N")); + private readonly TempFolder temp = new TempFolder("snp-compose"); private readonly List log = new List(); - public void Dispose() - { - if (Directory.Exists(folder)) - { - Directory.Delete(folder, recursive: true); - } - } + private string folder => temp.Path; + + public void Dispose() => temp.Dispose(); private Settings Composing(CatalogEntry task) => new Settings { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/EvaluationAccountingTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/EvaluationAccountingTests.cs index 560967a..7371473 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/EvaluationAccountingTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/EvaluationAccountingTests.cs @@ -1,15 +1,10 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Simulation; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Modules; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; namespace SnpEvolution.Tests.Application { @@ -63,47 +58,27 @@ public void ARunStopsOnceItsEvaluationBudgetIsSpent() Assert.InRange(evaluations.Networks, 300, 300 + Population * (1 + new ModulePolicy().SideGenerations + new ModulePolicy().IncubationGenerations)); } - [Fact] - public void RetestsOfASolvedNetworkCountAsVerification() - { - var counter = new EvaluationBudget(); - var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), FunctionTask.Of("n", n => n, new[] { 1, 2 }), - new SimulationOptions(40, 5, OutputTiming.Interval), 3, new Random(1), counter, EvaluationSource.SideRun); - - evaluator.Evaluate(TestNetworks.Identity()); - Assert.True(evaluator.ConfirmSolved(TestNetworks.Identity()).Solved); - - Assert.Equal(1, counter[EvaluationSource.SideRun]); - Assert.Equal(3, counter[EvaluationSource.Verification]); - } - // Composition search reads its parts from the library folder, and their recorded cost is reported as paid up front. [Fact] public void ACompositionRunCountsTheLibrarysPartsAsAnUpFrontCost() { - string folder = Path.Combine(Path.GetTempPath(), "snp-composition-" + Guid.NewGuid()); - try - { - var library = new ModuleLibrary(); - library.AddPart(Verifier.Measure(ReferenceParts.Delay(2), new EvaluationBudget()).ToLibraryPart(ReferenceParts.Delay(2), new PartOrigin(1, "a test", 1234)), "a test"); - PartLibraryFiles.Save(library, folder); - Settings settings = Stalling(5); - settings.Modules = false; - settings.PartLibraryFolder = folder; - settings.Algorithm = SearchCatalog.CompositionMapElites; - var evaluations = new EvaluationBudget(); + using var temp = new TempFolder("snp-composition"); + string folder = temp.Path; + var library = new ModuleLibrary(); + library.AddPart(PartFixtures.Measured(ReferenceParts.Delay(2), new PartOrigin(1, "a test", 1234)), "a test"); + PartLibraryFiles.Save(library, folder); + Settings settings = Stalling(5); + settings.Modules = false; + settings.PartLibraryFolder = folder; + settings.Algorithm = SearchCatalog.CompositionMapElites; + var evaluations = new EvaluationBudget(); - IGeneticAlgorithm run = EvolutionSession.Evolve(settings, settings.SelectedTask, factory => factory.NewNetwork(), new Random(3), _ => { }, evaluations); + IGeneticAlgorithm run = EvolutionSession.Evolve(settings, settings.SelectedTask, factory => factory.NewNetwork(), new Random(3), _ => { }, evaluations); - Assert.Equal(1234, evaluations.UpFront); - Assert.Equal(5L * Population, evaluations.Networks); - Assert.Contains(run.Population, individual => individual.Genes.Neurons.Any(neuron => neuron.Module != null)); - Assert.Contains("1,234", evaluations.Report().Describe()); - } - finally - { - Directory.Delete(folder, recursive: true); - } + Assert.Equal(1234, evaluations.UpFront); + Assert.Equal(5L * Population, evaluations.Networks); + Assert.Contains(run.Population, individual => individual.Genes.Neurons.Any(neuron => neuron.Module != null)); + Assert.Contains("1,234", evaluations.Report().Describe()); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/EvolveServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/EvolveServiceTests.cs index 08bd60a..b231543 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/EvolveServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/EvolveServiceTests.cs @@ -1,5 +1,5 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; namespace SnpEvolution.Tests.Application { @@ -8,22 +8,15 @@ public class EvolveServiceTests [Fact] public void ACompositionRunOnABrokenLibraryIsATypedErrorNamingTheFile() { - string folder = Path.Combine(Path.GetTempPath(), "snp-evolve-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(folder); - try - { - File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); - var settings = new Settings { Algorithm = SearchCatalog.CompositionTournament, PartLibraryFolder = folder }; + using var temp = new TempFolder("snp-evolve").Made(); + string folder = temp.Path; + File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); + var settings = new Settings { Algorithm = SearchCatalog.CompositionTournament, PartLibraryFolder = folder }; - EvolveResult result = EvolveService.Run(new EvolveRequest(settings, new Random(1), "Composed", Folder: folder), _ => { }); + EvolveResult result = EvolveService.Run(new EvolveRequest(settings, new Random(1), "Composed", Folder: folder), _ => { }); - Assert.Null(result.Run); - Assert.Contains("delay-2.json", result.Error); - } - finally - { - Directory.Delete(folder, recursive: true); - } + Assert.Null(result.Run); + Assert.Contains("delay-2.json", result.Error); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/ExportServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/ExportServiceTests.cs new file mode 100644 index 0000000..cd876a4 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/ExportServiceTests.cs @@ -0,0 +1,18 @@ +using SnpEvolution.Application; +using SnpEvolution.Storage; + +namespace SnpEvolution.Tests.Application +{ + public class ExportServiceTests + { + [Fact] + public void AnExportedNetworkRunsForTheStepsGiven() + { + using var temp = new TempFolder("snp-network").Made(); + string file = Path.Combine(temp.Path, "network.json"); + NetworkFiles.Save(TestNetworks.PingPong(), file); + + Assert.Equal(7, Assert.Single(ExportService.Network(file, 7).Value!.Cases).Steps); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/OutputTargetTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/OutputTargetTests.cs new file mode 100644 index 0000000..6942c9d --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/OutputTargetTests.cs @@ -0,0 +1,49 @@ +using SnpEvolution.Application; +using SnpEvolution.Specs.Tasks; + +namespace SnpEvolution.Tests.Application +{ + public class OutputTargetTests + { + [Theory] + [InlineData("1,1,2,3,5,8,13", new[] { 1, 1, 2, 3, 5, 8, 13 })] + [InlineData("1, 1, 2, 3, 5 ...", new[] { 1, 1, 2, 3, 5 })] + [InlineData("{2 4 6}", new[] { 2, 4, 6 })] + [InlineData("[3,1,4]…", new[] { 3, 1, 4 })] + public void NumbersParseWithAnySeparatorsBracketsAndEllipsis(string input, int[] expected) + { + Assert.True(OutputTarget.TryParse(TargetKind.Sequence, input, out OutputTarget target)); + Assert.Equal(expected, target.Values); + } + + [Theory] + [InlineData("")] + [InlineData("1,0,2")] + [InlineData("1,-2")] + [InlineData("1,x")] + public void NumbersMustBePositiveIntegers(string input) + { + Assert.False(OutputTarget.TryParse(TargetKind.Set, input, out _)); + } + + [Fact] + public void BinaryWordsIgnoreSpacingAndNeedAOne() + { + Assert.True(OutputTarget.TryParse(TargetKind.BinaryWord, "0110 1001_0", out OutputTarget target)); + Assert.Equal(new[] { 0, 1, 1, 0, 1, 0, 0, 1, 0 }, target.Values); + Assert.False(OutputTarget.TryParse(TargetKind.BinaryWord, "0000", out _)); + Assert.False(OutputTarget.TryParse(TargetKind.BinaryWord, "0120", out _)); + } + + [Fact] + public void EachKindMakesItsTask() + { + var fitness = new JaccardFitness(new[] { 1 }); + + Assert.IsType(new OutputTarget(TargetKind.Set, new[] { 1, 1, 2 }).CreateTask(fitness)); + Assert.IsType(new OutputTarget(TargetKind.Sequence, new[] { 1, 1, 2 }).CreateTask(fitness)); + Assert.IsType(new OutputTarget(TargetKind.BinaryWord, new[] { 0, 1 }).CreateTask(fitness)); + } + + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/PartLibrariesTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/PartLibrariesTests.cs index e73f4e4..6a5aafb 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/PartLibrariesTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/PartLibrariesTests.cs @@ -1,5 +1,5 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Parts; +using SnpEvolution.Specs.Parts; namespace SnpEvolution.Tests.Application { @@ -9,7 +9,8 @@ public class PartLibrariesTests [Slow] public void HandBuiltLeavesLeaveOutThePromotedAddLoop() { - string missing = Path.Combine(Path.GetTempPath(), "snp-no-parts-" + Guid.NewGuid().ToString("N")); + using var temp = new TempFolder("snp-no-parts"); + string missing = temp.Path; ModuleLibrary leaves = PartLibraries.Load(missing, handBuilt: true, addLoop: false).Value!; ModuleLibrary withLoop = PartLibraries.Load(missing, handBuilt: true, addLoop: true).Value!; @@ -22,21 +23,14 @@ public void HandBuiltLeavesLeaveOutThePromotedAddLoop() [Fact] public void ABrokenLibraryIsATypedErrorNamingTheFile() { - string folder = Path.Combine(Path.GetTempPath(), "snp-broken-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(folder); - try - { - File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); + using var temp = new TempFolder("snp-broken").Made(); + string folder = temp.Path; + File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); - Loaded loaded = PartLibraries.Load(folder, handBuilt: false, addLoop: false); + Loaded loaded = PartLibraries.Load(folder, handBuilt: false, addLoop: false); - Assert.Null(loaded.Value); - Assert.Contains("delay-2.json", loaded.Error); - } - finally - { - Directory.Delete(folder, recursive: true); - } + Assert.Null(loaded.Value); + Assert.Contains("delay-2.json", loaded.Error); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/PartsServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/PartsServiceTests.cs index 869108c..93d64d3 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/PartsServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/PartsServiceTests.cs @@ -1,20 +1,16 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Specs.Contracts; using SnpEvolution.Storage; namespace SnpEvolution.Tests.Application { public sealed class PartsServiceTests : IDisposable { - private readonly string folder = Path.Combine(Path.GetTempPath(), "parts-session-" + Guid.NewGuid().ToString("N")); + private readonly TempFolder temp = new TempFolder("parts-session"); - public void Dispose() - { - if (Directory.Exists(folder)) - { - Directory.Delete(folder, recursive: true); - } - } + private string folder => temp.Path; + + public void Dispose() => temp.Dispose(); private static readonly IReadOnlyList Delays = new[] { FirstParts.Named("delay 2"), FirstParts.Named("delay 4") }; diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/ReachServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/ReachServiceTests.cs index ce937b1..07f0373 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/ReachServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/ReachServiceTests.cs @@ -1,7 +1,5 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; namespace SnpEvolution.Tests.Application @@ -41,25 +39,19 @@ public void WithoutABudgetNothingRunsSinceTheSetupsAreComparedOnIt() => [Slow] public void CompositionSearchSpendsOnlyWhatThePartsLeaveOfTheSharedBudget() { - string folder = Path.Combine(Path.GetTempPath(), "snp-reach-" + Guid.NewGuid()); - try - { - var library = new ModuleLibrary(); - library.AddPart(Verifier.Measure(ReferenceParts.Delay(2), new EvaluationBudget()).ToLibraryPart(ReferenceParts.Delay(2), new PartOrigin(1, "a test", PartCost)), "a test"); - PartLibraryFiles.Save(library, folder); - Settings settings = Budgeted(folder, PartCost + 100); - - ReachService.Outcome composition = ReachService.RunOnce(settings, Named("composition"), 1, PartCost); - ReachService.Outcome flat = ReachService.RunOnce(settings, Named("flat"), 1, PartCost); - - Assert.Equal(PartCost, composition.UpFront); - Assert.InRange(composition.Evaluations, 100, PartCost - 1); - Assert.True(flat.Evaluations >= PartCost + 100); - } - finally - { - Directory.Delete(folder, recursive: true); - } + using var temp = new TempFolder("snp-reach"); + string folder = temp.Path; + var library = new ModuleLibrary(); + library.AddPart(PartFixtures.Measured(ReferenceParts.Delay(2), new PartOrigin(1, "a test", PartCost)), "a test"); + PartLibraryFiles.Save(library, folder); + Settings settings = Budgeted(folder, PartCost + 100); + + ReachService.Outcome composition = ReachService.RunOnce(settings, Named("composition"), 1, PartCost); + ReachService.Outcome flat = ReachService.RunOnce(settings, Named("flat"), 1, PartCost); + + Assert.Equal(PartCost, composition.UpFront); + Assert.InRange(composition.Evaluations, 100, PartCost - 1); + Assert.True(flat.Evaluations >= PartCost + 100); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/RunFoldersTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/RunFoldersTests.cs index 4fca44a..f94032e 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/RunFoldersTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/RunFoldersTests.cs @@ -2,26 +2,21 @@ namespace SnpEvolution.Tests.Application { - public class WorkingRootTests + public class RunFoldersTests { [Fact] public void AGitFolderOrAWorktreesGitFileMarksTheRoot() { - string folder = Path.Combine(Path.GetTempPath(), "snp-root-" + Guid.NewGuid().ToString("N")); + using var temp = new TempFolder("snp-root"); + string folder = temp.Path; string inside = Path.Combine(folder, "repository", "worktree", "deep"); Directory.CreateDirectory(inside); - try - { - Directory.CreateDirectory(Path.Combine(folder, "repository", ".git")); - Assert.Equal(Path.Combine(folder, "repository"), RunFolders.RepositoryRootAbove(inside)); - File.WriteAllText(Path.Combine(folder, "repository", "worktree", ".git"), "gitdir: elsewhere"); - Assert.Equal(Path.Combine(folder, "repository", "worktree"), RunFolders.RepositoryRootAbove(inside)); - } - finally - { - Directory.Delete(folder, recursive: true); - } + Directory.CreateDirectory(Path.Combine(folder, "repository", ".git")); + Assert.Equal(Path.Combine(folder, "repository"), RunFolders.RepositoryRootAbove(inside)); + + File.WriteAllText(Path.Combine(folder, "repository", "worktree", ".git"), "gitdir: elsewhere"); + Assert.Equal(Path.Combine(folder, "repository", "worktree"), RunFolders.RepositoryRootAbove(inside)); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/TargetPatternsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/TargetPatternsTests.cs new file mode 100644 index 0000000..531846a --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/TargetPatternsTests.cs @@ -0,0 +1,36 @@ +using SnpEvolution.Application; + +namespace SnpEvolution.Tests.Application +{ + public class TargetPatternsTests + { + [Fact] + public void FindsTheTypoInAFibonacciTarget() + { + SuspectedTypo? typo = TargetPatterns.Find(new[] { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 114, 233, 377, 610, 987 }); + + Assert.NotNull(typo); + Assert.Equal((11, 114, 144), (typo!.Index, typo.Found, typo.Expected)); + } + + [Theory] + [InlineData(new[] { 2, 4, 6, 9, 10 }, 3, 8)] + [InlineData(new[] { 1, 2, 4, 8, 15, 32 }, 4, 16)] + [InlineData(new[] { 7, 1, 2, 3, 5, 8 }, 0, 1)] + public void FindsASingleValueThatBreaksAPattern(int[] values, int index, int expected) + { + SuspectedTypo? typo = TargetPatterns.Find(values); + + Assert.Equal((index, expected), (typo!.Index, typo.Expected)); + } + + [Theory] + [InlineData(new[] { 1, 1, 2, 3, 5, 8, 13 })] + [InlineData(new[] { 3, 1, 4, 1, 5, 9 })] + [InlineData(new[] { 1, 1, 2, 4 })] + public void LeavesTargetsWithoutAClearTypoAlone(int[] values) + { + Assert.Null(TargetPatterns.Find(values)); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/CommandArgsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/CommandArgsTests.cs index c49f0d3..461abd2 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/CommandArgsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/CommandArgsTests.cs @@ -1,9 +1,10 @@ using SnpEvolution.Application; using SnpEvolution.Cli; -using SnpEvolution.Evolution.Modules; +using SnpEvolution.Search.Modules; namespace SnpEvolution.Tests.Cli { + [Collection(ProcessStateCollection.Name)] public class CommandArgsTests { private static string Problem(params string[] line) @@ -59,8 +60,12 @@ public void AWordThatIsNotAnOptionIsRefused() => [InlineData("evolve", "--target", "1,2", "--glue-weight", "-1")] [InlineData("compose", "--task", "no such task")] [InlineData("evolve", "--target", "1,2", "--algorithm", "no such search")] - public void BadCommandLinesExitWithUsage(params string[] line) => + public void BadCommandLinesExitWithUsage(params string[] line) + { + using var console = new ConsoleCapture(); + Assert.Equal((int)ExitCode.Usage, CommandLine.Run(line)); + } [Fact] public void BudgetAndCompositionOptionsReachTheSettings() diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/ExportCommandsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/ExportCommandsTests.cs index 48bb6d3..70b8d4b 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/ExportCommandsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/ExportCommandsTests.cs @@ -1,57 +1,44 @@ using SnpEvolution.Application; using SnpEvolution.Cli; -using SnpEvolution.Storage; namespace SnpEvolution.Tests.Cli { + [Collection(ProcessStateCollection.Name)] public class ExportCommandsTests { - private static string Folder() => Path.Combine(Path.GetTempPath(), "snp-export-" + Guid.NewGuid().ToString("N")); - [Fact] public void ExportVerilogWritesTheDesignTestbenchAndExpectedTrace() { - string folder = Folder(); - try - { - int exit = CommandLine.Run(new[] { "export-verilog", "--part", RepositoryFiles.PartFile("parts", "delay-2.json"), "--out", folder, "--check", "off" }); - - Assert.Equal(0, exit); - Assert.All(new[] { "delay_2.v", "delay_2_tb.v", "delay_2_expected.txt" }, file => Assert.True(File.Exists(Path.Combine(folder, file)), file)); - } - finally - { - if (Directory.Exists(folder)) - { - Directory.Delete(folder, recursive: true); - } - } + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-export"); + string folder = temp.Path; + + int exit = CommandLine.Run(new[] { "export-verilog", "--part", RepositoryFiles.PartFile("parts", "delay-2.json"), "--out", folder, "--check", "off" }); + + Assert.Equal(0, exit); + Assert.All(new[] { "delay_2.v", "delay_2_tb.v", "delay_2_expected.txt" }, file => Assert.True(File.Exists(Path.Combine(folder, file)), file)); } [Fact] public void ExportUppaalWritesTheModelAndItsQueries() { - string folder = Folder(); - try - { - int exit = CommandLine.Run(new[] { "export-uppaal", "--part", RepositoryFiles.PartFile("parts", "delay-2.json"), "--out", folder, "--check", "off" }); - - Assert.Equal(0, exit); - Assert.All(new[] { "delay-2.xml", "delay-2.q" }, file => Assert.True(File.Exists(Path.Combine(folder, file)), file)); - } - finally - { - if (Directory.Exists(folder)) - { - Directory.Delete(folder, recursive: true); - } - } + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-export"); + string folder = temp.Path; + + int exit = CommandLine.Run(new[] { "export-uppaal", "--part", RepositoryFiles.PartFile("parts", "delay-2.json"), "--out", folder, "--check", "off" }); + + Assert.Equal(0, exit); + Assert.All(new[] { "delay-2.xml", "delay-2.q" }, file => Assert.True(File.Exists(Path.Combine(folder, file)), file)); } [Fact] public void ExportNirRefusesAPartOutsideTheProfile() { - string folder = Folder(); + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-export"); + string folder = temp.Path; + int exit = CommandLine.Run(new[] { "export-nir", "--part", RepositoryFiles.PartFile("parts", "delay-2.json"), "--out", folder }); @@ -63,23 +50,16 @@ public void ExportNirRefusesAPartOutsideTheProfile() [Slow] public void EvolvePartsUnderTheProfileSavesAProfilePart() { - string folder = Folder(); - try - { - int exit = CommandLine.Run(new[] { "evolve-parts", "--only", "delay 1", "--budget", "5000", "--profile", "hardware", "--library", folder }); - - Assert.Equal(0, exit); - var part = SnpEvolution.Storage.PartLibraryFiles.Read(File.ReadAllText(Path.Combine(folder, "delay-1.json")), "delay-1.json"); - Assert.Empty(SnpEvolution.Networks.HardwareProfile.Problems(part.Part.Network)); - Assert.EndsWith("--profile hardware", part.Origin.Run); - } - finally - { - if (Directory.Exists(folder)) - { - Directory.Delete(folder, recursive: true); - } - } + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-export"); + string folder = temp.Path; + + int exit = CommandLine.Run(new[] { "evolve-parts", "--only", "delay 1", "--budget", "5000", "--profile", "hardware", "--library", folder }); + + Assert.Equal(0, exit); + var part = SnpEvolution.Storage.PartLibraryFiles.Read(File.ReadAllText(Path.Combine(folder, "delay-1.json")), "delay-1.json"); + Assert.Empty(SnpEvolution.Model.HardwareProfile.Problems(part.Part.Network)); + Assert.EndsWith("--profile hardware", part.Origin.Run); } [Fact] @@ -90,26 +70,13 @@ public void ACheckThatDiffersFromOurEngineHasItsOwnExitCode() Assert.Equal(ExitCode.Differs, ExportCommand.ExitFor(ExportStatus.Differs)); } - [Fact] - public void AnExportedNetworkRunsForTheStepsGiven() - { - string file = Path.Combine(Path.GetTempPath(), "snp-network-" + Guid.NewGuid().ToString("N") + ".json"); - try - { - NetworkFiles.Save(TestNetworks.PingPong(), file); - - Assert.Equal(7, Assert.Single(ExportService.Network(file, 7).Value!.Cases).Steps); - } - finally - { - File.Delete(file); - } - } - [Fact] public void ExportNeedsAPartOrANetwork() { - Assert.Equal(1, CommandLine.Run(new[] { "export-verilog", "--out", Folder() })); + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-export"); + + Assert.Equal(1, CommandLine.Run(new[] { "export-verilog", "--out", temp.Path })); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/ListCommandsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/ListCommandsTests.cs index 4394177..f87e83c 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/ListCommandsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/ListCommandsTests.cs @@ -1,8 +1,9 @@ -using SnpEvolution.Evolution.Benchmarking; +using SnpEvolution.Search.Benchmarking; using SnpEvolution.Tests.Golden; namespace SnpEvolution.Tests.Cli { + [Collection(ProcessStateCollection.Name)] public class ListCommandsTests { [Fact] diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/SavedRunsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/SavedRunsTests.cs index 5c56ca5..97363fe 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/SavedRunsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/SavedRunsTests.cs @@ -6,17 +6,11 @@ namespace SnpEvolution.Tests.Cli { public class SavedRunsTests : IDisposable { - private readonly string folder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + private readonly TempFolder folder = new TempFolder("snp-saved-runs"); - private SavedRuns Store => new SavedRuns(Path.Combine(folder, "saved-runs.json")); + private SavedRuns Store => new SavedRuns(Path.Combine(folder.Path, "saved-runs.json")); - public void Dispose() - { - if (Directory.Exists(folder)) - { - Directory.Delete(folder, recursive: true); - } - } + public void Dispose() => folder.Dispose(); private static SavedRun Run(string name, Settings settings) => new SavedRun(name, DateTime.Now, RunStart.Scratch, "TargetNet", "solved", settings); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/VerifyCommandTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/VerifyCommandTests.cs index 0eb8314..f876dfd 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/VerifyCommandTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/VerifyCommandTests.cs @@ -1,86 +1,62 @@ using SnpEvolution.Cli; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; -using SnpEvolution.Tests.Evolution; namespace SnpEvolution.Tests.Cli { + [Collection(ProcessStateCollection.Name)] public class VerifyCommandTests { - private static string Folder() => Path.Combine(Path.GetTempPath(), "snp-verify-" + Guid.NewGuid().ToString("N")); - [Fact] [Slow] public void VerifyRecordsTheProvenBoundInEachPartFile() { - string folder = Folder(); - Directory.CreateDirectory(folder); - try - { - File.Copy(RepositoryFiles.PartFile("parts", "delay-2.json"), Path.Combine(folder, "delay-2.json")); + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-verify").Made(); + string folder = temp.Path; + File.Copy(RepositoryFiles.PartFile("parts", "delay-2.json"), Path.Combine(folder, "delay-2.json")); - int exit = CommandLine.Run(new[] { "verify", "--library", folder, "--seconds", "10" }); + int exit = CommandLine.Run(new[] { "verify", "--library", folder, "--seconds", "10" }); - Assert.Equal(0, exit); - ProvenBound? proven = PartLibraryFiles.Load(folder).Parts.Single().Part!.Proven; - Assert.True(proven?.AllInputs); - } - finally - { - Directory.Delete(folder, recursive: true); - } + Assert.Equal(0, exit); + ProvenBound? proven = PartLibraryFiles.Load(folder).Parts.Single().Part!.Proven; + Assert.True(proven?.AllInputs); } [Fact] [Slow] public void OnlyVerifiesThePartsWhoseContractNamesMatch() { - string folder = Folder(); - Directory.CreateDirectory(folder); - try + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-verify").Made(); + string folder = temp.Path; + foreach (string file in new[] { "delay-2.json", "sequencer-2.json" }) { - foreach (string file in new[] { "delay-2.json", "sequencer-2.json" }) - { - LibraryPart unproven = RepositoryFiles.ReadPart("parts", file) with { Proven = null }; - File.WriteAllText(Path.Combine(folder, file), PartLibraryFiles.ToJson(unproven)); - } + LibraryPart unproven = RepositoryFiles.ReadPart("parts", file) with { Proven = null }; + File.WriteAllText(Path.Combine(folder, file), PartLibraryFiles.ToJson(unproven)); + } - int exit = CommandLine.Run(new[] { "verify", "--library", folder, "--only", "DELAY", "--seconds", "10" }); + int exit = CommandLine.Run(new[] { "verify", "--library", folder, "--only", "DELAY", "--seconds", "10" }); - Assert.Equal(0, exit); - Dictionary proven = PartLibraryFiles.Load(folder).Parts.Select(module => module.Part!).ToDictionary(part => part.Contract.Name, part => part.Proven); - Assert.NotNull(proven["delay 2"]); - Assert.Null(proven["sequencer 2"]); - } - finally - { - Directory.Delete(folder, recursive: true); - } + Assert.Equal(0, exit); + Dictionary proven = PartLibraryFiles.Load(folder).Parts.Select(module => module.Part!).ToDictionary(part => part.Contract.Name, part => part.Proven); + Assert.NotNull(proven["delay 2"]); + Assert.Null(proven["sequencer 2"]); } [Fact] public void VerifyExitsWithTwoAndRecordsTheCounterexample() { - string folder = Folder(); - Directory.CreateDirectory(folder); - string file = Path.Combine(folder, "register.json"); - try - { - Part broken = BoundedCheckTests.RegisterFailingAtTwenty(); - File.WriteAllText(file, PartLibraryFiles.ToJson(Verifier.Measure(broken, new EvaluationBudget()).ToLibraryPart(broken, new PartOrigin(0, "by hand", 0)))); + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-verify").Made(); + string file = Path.Combine(temp.Path, "register.json"); + Part broken = PartFixtures.RegisterFailingAtTwenty(); + File.WriteAllText(file, PartLibraryFiles.ToJson(PartFixtures.Measured(broken, new PartOrigin(0, "by hand", 0)))); - int exit = CommandLine.Run(new[] { "verify", "--part", file, "--bound", "24" }); + int exit = CommandLine.Run(new[] { "verify", "--part", file, "--bound", "24" }); - Assert.Equal((int)ExitCode.Refuted, exit); - Assert.Contains("\"FailsAt\": \"n=20\"", File.ReadAllText(file)); - } - finally - { - Directory.Delete(folder, recursive: true); - } + Assert.Equal((int)ExitCode.Refuted, exit); + Assert.Contains("\"FailsAt\": \"n=20\"", File.ReadAllText(file)); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Compilation/CompilerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Compilation/CompilerTests.cs deleted file mode 100644 index 2ce816a..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Compilation/CompilerTests.cs +++ /dev/null @@ -1,213 +0,0 @@ -using SnpEvolution.Compilation; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; -using SnpEvolution.Simulation; - -namespace SnpEvolution.Tests.Compilation -{ - public class CompilerTests - { - private static readonly int[] Fibonacci = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584 }; - - private static FitnessEvaluator SequenceEvaluator(IReadOnlyList values) => - new FitnessEvaluator(new SequentialCpuEngine(), new SequenceTask("target", values), new SimulationOptions(50, 2, OutputTiming.Interval), 1, new Random(1), new EvaluationBudget()); - - private static IReadOnlyList Generated(Network network, int maxSteps = 400) => - new ExhaustiveCpuEngine().CollectOutputs(new[] { network }, new SimulationOptions(maxSteps, 20, OutputTiming.Interval), new Random(1))[0]; - - [Fact] - public void FitsFibonacciAsTheSumOfTheTwoValuesBefore() - { - Recurrence? recurrence = Recurrence.Fit(Fibonacci.Take(16).ToList()); - - Assert.NotNull(recurrence); - Assert.Equal(new[] { 1, 1 }, recurrence!.Coefficients); - Assert.Equal(new[] { 1, 1 }, recurrence.Initial); - } - - [Fact] - public void FindsNoRecurrenceForASequenceWithoutOne() - { - Assert.Null(Recurrence.Fit(new[] { 1, 2, 1, 3, 1, 4, 1, 5 })); - } - - // The network is fitted to 16 values and keeps going: the next two are right as well. - [Fact] - public void CompiledFibonacciMakesExactlyTheGapsAndContinuesPastTheTarget() - { - Recurrence recurrence = Recurrence.Fit(Fibonacci.Take(16).ToList())!; - Network network = RecurrenceCompiler.Compile(recurrence); - - FitnessResult result = SequenceEvaluator(Fibonacci).Evaluate(network); - - Assert.Equal(1f, result.Fitness); - Assert.Equal(15, network.Neurons.Count); - Assert.All(network.Neurons, neuron => Assert.True(neuron.Rules.Count <= 3)); - } - - [Theory] - [InlineData(new[] { 1, 2, 4, 8, 16, 32, 64 })] - [InlineData(new[] { 1, 2, 5, 12, 29, 70, 169 })] - [InlineData(new[] { 1, 1, 2, 4, 7, 13, 24, 44, 81 })] - [InlineData(new[] { 3, 3, 3, 3, 3, 3 })] - [InlineData(new[] { 2, 1, 3, 4, 7, 11, 18, 29 })] - [InlineData(new[] { 5, 1, 1, 2, 3, 5, 8, 13 })] - public void CompiledRecurrencesMakeTheirSequences(int[] values) - { - Recurrence recurrence = Recurrence.Fit(values)!; - - Assert.Equal(1f, SequenceEvaluator(values).Evaluate(RecurrenceCompiler.Compile(recurrence)).Fitness); - } - - [Fact] - public void ProgramsReadAndWriteTheSameWay() - { - RegisterProgram program = RegisterProgram.Parse("0: ADD r1 -> 1 | 2\n1: SUB r1 -> 2 else 3\n2: ADD r0 -> 3\n3: HALT\n"); - - Assert.Equal(program.ToString(), RegisterProgram.Parse(program.ToString()).ToString()); - Assert.Equal(2, program.RegisterCount); - } - - [Fact] - public void ProgramsGenerateEveryNumberAlongEveryChoice() - { - RegisterProgram evens = RegisterProgram.Parse("ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT"); - - (IReadOnlyList outputs, bool complete, _) = evens.Generate(maxSteps: 20); - - Assert.Equal(new[] { 2, 4, 6, 8, 10, 12 }, outputs.Take(6)); - Assert.False(complete); - } - - [Fact] - public void TheOutputRegisterCannotBeSubtractedFrom() - { - Assert.NotNull(RegisterProgram.Parse("SUB r0 -> 1 else 1\nHALT").Problem()); - } - - [Theory] - [InlineData("ADD r0 -> 1\nADD r0 -> 2\nADD r0 -> 3\nHALT", new[] { 3 })] - [InlineData("ADD r0 -> 1\nADD r0 -> 2 | 3\nADD r0 -> 3\nHALT", new[] { 2, 3 })] - // Counts 2 or 3 into r1, then moves it into r0 one at a time. - [InlineData("ADD r1 -> 1\nADD r1 -> 2 | 3\nADD r1 -> 3\nSUB r1 -> 4 else 5\nADD r0 -> 3\nHALT", new[] { 2, 3 })] - // Two registers subtracted from by different instructions, with a choice inside the loop. - [InlineData("ADD r1 -> 1\nADD r2 -> 2 | 3\nADD r2 -> 3\nSUB r2 -> 4 else 5\nADD r0 -> 3\nSUB r1 -> 6 else 7\nADD r0 -> 5\nHALT", new[] { 2, 3 })] - public void CompiledProgramsGenerateWhatTheProgramDoes(string text, int[] expected) - { - RegisterProgram program = RegisterProgram.Parse(text); - Assert.Equal(expected, program.Generate().Outputs); - - Assert.Equal(expected, Generated(RegisterMachineCompiler.Compile(program))); - } - - // Outputs a, then (a, b) becomes (b, a + b): every Fibonacci number, with r4 only there to make the choice. - private const string FibonacciProgram = @" - 0: ADD r1 -> 1 - 1: ADD r2 -> 2 - 2: ADD r4 -> 3 | 5 - 3: SUB r1 -> 4 else 12 - 4: ADD r0 -> 3 - 5: SUB r1 -> 6 else 7 - 6: ADD r3 -> 5 - 7: SUB r2 -> 8 else 10 - 8: ADD r1 -> 9 - 9: ADD r3 -> 7 - 10: SUB r3 -> 11 else 2 - 11: ADD r2 -> 10 - 12: HALT"; - - [Fact] - public void ACompiledFibonacciProgramGeneratesTheSameNumbersAsTheProgram() - { - RegisterProgram program = RegisterProgram.Parse(FibonacciProgram); - (IReadOnlyList outputs, _, int steps) = program.Generate(outputLimit: 21, valueLimit: 100); - - IReadOnlyList generated = Generated(RegisterMachineCompiler.Compile(program), 4 * (steps + 2) + 31); - - Assert.Equal(new[] { 1, 2, 3, 5, 8, 13, 21 }, outputs); - Assert.Equal(outputs, generated.Where(number => number <= 21)); - } - - [Fact] - public void ProgramSearchFindsASmallSetAndItsNetworkGeneratesIt() - { - var search = new ProgramSearch(new ProgramSearchSettings(Population: 60)); - var target = new GeneratorTask("{2,3}", new[] { 2, 3 }, new JaccardFitness(new[] { 2, 3 })); - var budget = new EvaluationBudget(); - - SearchOutcome best = search.Run(new SearchRequest(target, budget, new Random(1), _ => { }) { MaxGenerations = 400 }); - - Assert.True(best.Solved, best.Best!.ToString()); - Assert.Equal(new[] { 2, 3 }, Generated(RegisterMachineCompiler.Compile(best.Best!))); - Assert.True(budget[EvaluationKind.InterpreterRun] > 0); - } - - [Fact] - public void ProgramSearchLearnsLargeSetsInStagesThatEndPastTheLargestNumber() - { - Assert.Equal(new long[] { 5, 13, 34, 89, 233, 610, 1976 }, ProgramScoring.StageBounds(new[] { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 })); - Assert.Equal(new long[] { 14 }, ProgramScoring.StageBounds(new[] { 2, 4, 6 })); - } - - [Fact] - public void AStageChecksEveryTargetNumberUpToItsBoundAndNothingExtra() - { - var scoring = new ProgramScoring(new[] { 1, 2, 3, 5, 8 }, 2_000, new EvaluationBudget()); - - ScoredProgram scored = scoring.Score(RegisterProgram.Parse("ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT")); - - Assert.Equal(5, scored.Checks.Count); - } - - [Fact] - public void AnEditNeverLeavesAProgramWithoutInstructions() - { - var edits = new ProgramEdits(16, 4, new Random(1)); - RegisterProgram single = RegisterProgram.Parse("HALT"); - - Assert.All(Enumerable.Range(0, 300), _ => Assert.NotEmpty(edits.Mutate(single).Instructions)); - } - - // Only the first numbers are generated before the limit on register 0 cuts the computations off, and that - // still counts as complete, since nothing at or below the limit is lost. - [Fact] - public void TheOutputLimitLosesNothingBelowIt() - { - RegisterProgram evens = RegisterProgram.Parse("ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT"); - - (IReadOnlyList outputs, bool complete, _) = evens.Generate(outputLimit: 7); - - Assert.Equal(new[] { 2, 4, 6 }, outputs); - Assert.True(complete); - } - - [Fact] - [Slow] - public void ShrinkingKeepsTheNetworkCorrectAndNeverGrowsIt() - { - int[] values = { 1, 2, 4, 8, 16, 32 }; - Network compiled = RecurrenceCompiler.Compile(Recurrence.Fit(values)!); - FitnessEvaluator evaluator = SequenceEvaluator(values); - var random = new Random(5); - var space = new GenomeSpace(RuleForm: RuleForm.Standard, MaxNeurons: compiled.Neurons.Count, MaxInitialSpikes: 8, MaxProduce: 2); - var factory = new NetworkFactory(space, new ExpressionGenerator(ExpressionGenerator.SimpleTemplates, 4, random), random); - var setup = new NetworkSetup(20, 0.5f, factory, () => compiled, new NetworkScoring(() => new SequentialCpuEngine(), new SimulationOptions(50, 2, OutputTiming.Interval), 1)); - - SearchOutcome shrink = SearchCatalog.Shrink.Run(new SearchRequest(evaluator.Task, new EvaluationBudget(), random, _ => { }) - { - Seeds = new[] { new Individual(compiled) }, - MaxGenerations = 30, - Networks = setup, - }); - - Assert.Equal(30, shrink.Generations); - Assert.True(HardwareCost.Of(shrink.Best!.Genes).CompareTo(HardwareCost.Of(compiled)) <= 0); - Assert.True(evaluator.ConfirmSolved(shrink.Best!.Genes).Solved); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RecurrenceCompilerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RecurrenceCompilerTests.cs new file mode 100644 index 0000000..81b3474 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RecurrenceCompilerTests.cs @@ -0,0 +1,54 @@ +using SnpEvolution.Compilation; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using static SnpEvolution.Tests.Fixtures.Runs; + +namespace SnpEvolution.Tests.Compilation +{ + public class RecurrenceCompilerTests + { + [Fact] + public void FitsFibonacciAsTheSumOfTheTwoValuesBefore() + { + Recurrence? recurrence = Recurrence.Fit(Sequences.Fibonacci.Take(16).ToList()); + + Assert.NotNull(recurrence); + Assert.Equal(new[] { 1, 1 }, recurrence!.Coefficients); + Assert.Equal(new[] { 1, 1 }, recurrence.Initial); + } + + [Fact] + public void FindsNoRecurrenceForASequenceWithoutOne() + { + Assert.Null(Recurrence.Fit(new[] { 1, 2, 1, 3, 1, 4, 1, 5 })); + } + + // The network is fitted to 16 values and keeps going: the next two are right as well. + [Fact] + public void CompiledFibonacciMakesExactlyTheGapsAndContinuesPastTheTarget() + { + Recurrence recurrence = Recurrence.Fit(Sequences.Fibonacci.Take(16).ToList())!; + Network network = RecurrenceCompiler.Compile(recurrence); + + FitnessResult result = SequenceEvaluator(Sequences.Fibonacci).Evaluate(network); + + Assert.Equal(1f, result.Fitness); + Assert.Equal(15, network.Neurons.Count); + Assert.All(network.Neurons, neuron => Assert.True(neuron.Rules.Count <= 3)); + } + + [Theory] + [InlineData(new[] { 1, 2, 4, 8, 16, 32, 64 })] + [InlineData(new[] { 1, 2, 5, 12, 29, 70, 169 })] + [InlineData(new[] { 1, 1, 2, 4, 7, 13, 24, 44, 81 })] + [InlineData(new[] { 3, 3, 3, 3, 3, 3 })] + [InlineData(new[] { 2, 1, 3, 4, 7, 11, 18, 29 })] + [InlineData(new[] { 5, 1, 1, 2, 3, 5, 8, 13 })] + public void CompiledRecurrencesMakeTheirSequences(int[] values) + { + Recurrence recurrence = Recurrence.Fit(values)!; + + Assert.Equal(1f, SequenceEvaluator(values).Evaluate(RecurrenceCompiler.Compile(recurrence)).Fitness); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterMachineCompilerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterMachineCompilerTests.cs new file mode 100644 index 0000000..8ab662e --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterMachineCompilerTests.cs @@ -0,0 +1,51 @@ +using SnpEvolution.Compilation; +using static SnpEvolution.Tests.Fixtures.Runs; + +namespace SnpEvolution.Tests.Compilation +{ + public class RegisterMachineCompilerTests + { + // Outputs a, then (a, b) becomes (b, a + b): every Sequences.Fibonacci number, with r4 only there to make the choice. + private const string FibonacciProgram = @" + 0: ADD r1 -> 1 + 1: ADD r2 -> 2 + 2: ADD r4 -> 3 | 5 + 3: SUB r1 -> 4 else 12 + 4: ADD r0 -> 3 + 5: SUB r1 -> 6 else 7 + 6: ADD r3 -> 5 + 7: SUB r2 -> 8 else 10 + 8: ADD r1 -> 9 + 9: ADD r3 -> 7 + 10: SUB r3 -> 11 else 2 + 11: ADD r2 -> 10 + 12: HALT"; + + [Theory] + [InlineData("ADD r0 -> 1\nADD r0 -> 2\nADD r0 -> 3\nHALT", new[] { 3 })] + [InlineData("ADD r0 -> 1\nADD r0 -> 2 | 3\nADD r0 -> 3\nHALT", new[] { 2, 3 })] + // Counts 2 or 3 into r1, then moves it into r0 one at a time. + [InlineData("ADD r1 -> 1\nADD r1 -> 2 | 3\nADD r1 -> 3\nSUB r1 -> 4 else 5\nADD r0 -> 3\nHALT", new[] { 2, 3 })] + // Two registers subtracted from by different instructions, with a choice inside the loop. + [InlineData("ADD r1 -> 1\nADD r2 -> 2 | 3\nADD r2 -> 3\nSUB r2 -> 4 else 5\nADD r0 -> 3\nSUB r1 -> 6 else 7\nADD r0 -> 5\nHALT", new[] { 2, 3 })] + public void CompiledProgramsGenerateWhatTheProgramDoes(string text, int[] expected) + { + RegisterProgram program = RegisterProgram.Parse(text); + Assert.Equal(expected, program.Generate().Outputs); + + Assert.Equal(expected, Generated(RegisterMachineCompiler.Compile(program))); + } + + [Fact] + public void ACompiledFibonacciProgramGeneratesTheSameNumbersAsTheProgram() + { + RegisterProgram program = RegisterProgram.Parse(FibonacciProgram); + (IReadOnlyList outputs, _, int steps) = program.Generate(outputLimit: 21, valueLimit: 100); + + IReadOnlyList generated = Generated(RegisterMachineCompiler.Compile(program), 4 * (steps + 2) + 31); + + Assert.Equal(new[] { 1, 2, 3, 5, 8, 13, 21 }, outputs); + Assert.Equal(outputs, generated.Where(number => number <= 21)); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs new file mode 100644 index 0000000..6354dc6 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs @@ -0,0 +1,46 @@ +using SnpEvolution.Compilation; + +namespace SnpEvolution.Tests.Compilation +{ + public class RegisterProgramTests + { + [Fact] + public void ProgramsReadAndWriteTheSameWay() + { + RegisterProgram program = RegisterProgram.Parse("0: ADD r1 -> 1 | 2\n1: SUB r1 -> 2 else 3\n2: ADD r0 -> 3\n3: HALT\n"); + + Assert.Equal(program.ToString(), RegisterProgram.Parse(program.ToString()).ToString()); + Assert.Equal(2, program.RegisterCount); + } + + [Fact] + public void ProgramsGenerateEveryNumberAlongEveryChoice() + { + RegisterProgram evens = RegisterProgram.Parse("ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT"); + + (IReadOnlyList outputs, bool complete, _) = evens.Generate(maxSteps: 20); + + Assert.Equal(new[] { 2, 4, 6, 8, 10, 12 }, outputs.Take(6)); + Assert.False(complete); + } + + [Fact] + public void TheOutputRegisterCannotBeSubtractedFrom() + { + Assert.NotNull(RegisterProgram.Parse("SUB r0 -> 1 else 1\nHALT").Problem()); + } + + // Only the first numbers are generated before the limit on register 0 cuts the computations off, and that + // still counts as complete, since nothing at or below the limit is lost. + [Fact] + public void TheOutputLimitLosesNothingBelowIt() + { + RegisterProgram evens = RegisterProgram.Parse("ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT"); + + (IReadOnlyList outputs, bool complete, _) = evens.Generate(outputLimit: 7); + + Assert.Equal(new[] { 2, 4, 6 }, outputs); + Assert.True(complete); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/AlgorithmTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/AlgorithmTests.cs deleted file mode 100644 index 6a42a6c..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/AlgorithmTests.cs +++ /dev/null @@ -1,164 +0,0 @@ -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Networks; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution -{ - public class AlgorithmTests - { - private static readonly BenchmarkSettings Settings = BenchmarkSettings.Default with { Seeds = 1 }; - - private static readonly BenchmarkTask Identity = TaskSuite.Functions.First(task => task.Name == "Compute n"); - - private sealed class DelegateEvaluator : IPopulationEvaluator - { - private readonly Func fitness; - - public DelegateEvaluator(Func fitness) => this.fitness = fitness; - - public int Evaluated { get; private set; } - - public IReadOnlyList EvaluateAll(IReadOnlyList networks) - { - Evaluated += networks.Count; - return networks.Select(network => new FitnessResult(fitness(network), Array.Empty())).ToList(); - } - } - - private static NetworkFactory Factory(Random random) => - new NetworkFactory(new GenomeSpace(RuleForm: RuleForm.Standard), new ExpressionGenerator(ExpressionGenerator.SimpleTemplates, 4, random), random); - - // Rewards networks for having exactly three neurons, which every algorithm should manage. - private static float ThreeNeurons(Network network) => 1f / (1 + Math.Abs(network.Neurons.Count - 3)); - - public static TheoryData StructuralAlgorithms => new TheoryData( - SearchCatalog.Evolution.Where(search => !search.EvolvesRulesOnly).Select(choice => choice.Name)); - - [Theory] - [Slow] - [MemberData(nameof(StructuralAlgorithms))] - public void StructuralAlgorithmsSolveTheIdentityFunction(string name) - { - EvolutionSearch algorithm = SearchCatalog.Evolution.Single(search => search.Name == name); - - RunOutcome outcome = Benchmark.RunOnce(algorithm, Identity, seed: 1, budget: 4000, Settings); - - Assert.True(outcome.Solved, $"{name} reached {outcome.BestFitness}"); - Assert.True(outcome.Best!.Exact); - } - - [Theory] - [MemberData(nameof(StructuralAlgorithms))] - public void BestNeverGetsWorseAndPopulationStaysFilled(string name) - { - var random = new Random(3); - NetworkFactory factory = Factory(random); - var evaluator = new DelegateEvaluator(ThreeNeurons); - IGeneticAlgorithm algorithm = SearchCatalog.Evolution.Single(search => search.Name == name) - .Create(new EvolutionContext(12, 0.5f, random, factory.NewNetwork, evaluator, factory, _ => { })); - - float best = float.MinValue; - for (int generation = 0; generation < 15; generation++) - { - algorithm.NextGeneration(); - Assert.True(algorithm.Best!.Fitness >= best); - best = algorithm.Best.Fitness; - Assert.NotEmpty(algorithm.Population); - } - - Assert.Equal(1f, best); - Assert.Equal(16, algorithm.Generation); - } - - [Fact] - public void RankingPrefersTheSmallerOfEquallyFitNetworks() - { - var small = new Individual(Identity()); - var large = new Individual(ReferenceNetworks.NaturalNumbers()); - small.Record(new FitnessResult(0.5f, Array.Empty())); - large.Record(new FitnessResult(0.5f, Array.Empty())); - - Assert.Same(small, Ranking.Rank(new[] { large, small })[0]); - - large.Record(new FitnessResult(0.6f, Array.Empty())); - Assert.Same(large, Ranking.Rank(new[] { small, large })[0]); - } - - [Fact] - public void MuPlusLambdaKeepsMuParents() - { - var random = new Random(0); - NetworkFactory factory = Factory(random); - var strategy = new MuPlusLambdaStrategy(3, 9, random, factory.NewNetwork, new DelegateEvaluator(ThreeNeurons), WeightedMutation.Structural(1, factory)); - - strategy.NextGeneration(); - strategy.NextGeneration(); - - Assert.Equal(3, strategy.Population.Count); - } - - [Fact] - public void MapElitesKeepsOneEliteForEachSize() - { - var random = new Random(0); - NetworkFactory factory = Factory(random); - var elites = new MapElites(10, random, factory.NewNetwork, new DelegateEvaluator(ThreeNeurons), new NeuronCrossover(), WeightedMutation.Structural(1, factory)); - - for (int generation = 0; generation < 10; generation++) - { - elites.NextGeneration(); - } - - Assert.Equal(elites.Population.Count, elites.Population.Select(elite => MapElites.Cell(elite.Genes)).Distinct().Count()); - Assert.True(elites.Population.Count > 3); - } - - [Fact] - public void SpeciesDistanceIsZeroOnlyForTheSameStructure() - { - Assert.Equal(0, SpeciatedAlgorithm.Distance(Identity(), Identity())); - Assert.True(SpeciatedAlgorithm.Distance(Identity(), ReferenceNetworks.NaturalNumbers()) > 2); - Assert.Equal(SpeciatedAlgorithm.Distance(Identity(), AlwaysOutputsOne()), SpeciatedAlgorithm.Distance(AlwaysOutputsOne(), Identity())); - } - - [Fact] - public void BenchmarkSummarisesRunsPerAlgorithmAndTask() - { - var best = new Individual(Identity()); - RunOutcome[] outcomes = - { - new RunOutcome("A", "T", 1, true, 100, 1, best), - new RunOutcome("A", "T", 2, true, 300, 1, best), - new RunOutcome("A", "T", 3, false, 1000, 0.5f, null), - }; - - BenchmarkRow row = Assert.Single(Benchmark.Summarise(outcomes)); - - Assert.Equal((3, 2, 200.0), (row.Runs, row.Solved, row.MedianEvaluationsToSolve!.Value)); - Assert.Equal(2.5 / 3, row.MeanBestFitness, precision: 5); - Assert.Equal(Identity().Size, row.MeanSolvedSize); - Assert.Contains("2/3", Benchmark.FormatTable(new[] { row })); - Assert.StartsWith("task,algorithm", Benchmark.FormatCsv(new[] { row })); - } - - [Fact] - [Slow] - public void SelectorHalvesTheCandidatesUntilOneIsLeft() - { - List candidates = SearchCatalog.Evolution.Take(3).ToList(); - - SelectionResult result = AlgorithmSelector.Select(candidates, TaskSuite.Generators[0], Settings, initialBudget: 60); - - Assert.Contains(result.Winner, candidates); - Assert.Equal(new[] { 3, 2 }, result.Rounds.Select(round => round.Standings.Count)); - Assert.Equal(new[] { 60L, 120L }, result.Rounds.Select(round => round.Budget)); - Assert.Equal(result.Rounds[0].Standings.Take(2).Select(row => row.Algorithm), result.Rounds[0].Advancing); - Assert.NotNull(result.BestFound); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/CheckScoringTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/CheckScoringTests.cs deleted file mode 100644 index 2a67332..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/CheckScoringTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Simulation; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution -{ - public class CheckScoringTests - { - [Fact] - public void SequenceChecksScoreEachGapInPlaceEvenAfterAMistake() - { - var task = new SequenceTask("test", new[] { 2, 1, 2 }); - var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(20, 2, OutputTiming.Interval), 1, new Random(1), new EvaluationBudget()); - - FitnessResult result = evaluator.Evaluate(PingPong()); - - Assert.Equal(new[] { 1f, 0f, 1f }, result.Checks); - Assert.Equal("gap 2 (1)", task.CheckName(1)); - Assert.Equal(new[] { 2, 1, 2 }, ((SequenceTask)task.Focus(1)!).Expected); - } - - [Fact] - public void LexicasePicksSpecialistsButNeverANetworkThatIsBestAtNothing() - { - var random = new Random(1); - List ranked = Ranking.Rank(new[] - { - Scored(PingPong(), 0.6f, 0.6f, 0.6f), - Scored(Identity(), 0.5f, 1f, 0f), - Scored(NeverOutputs(), 0.5f, 0f, 1f), - Scored(Chain(), 0.4f, 0.5f, 0.5f), - }); - Func pick = new LexicaseSelection().Prepare(ranked, random); - - List picks = Enumerable.Range(0, 200).Select(_ => pick()).ToList(); - - Assert.Contains(picks, individual => individual.Fitness == 0.5f && individual.Checks[1] == 1f); - Assert.Contains(picks, individual => individual.Fitness == 0.5f && individual.Checks[0] == 1f); - Assert.DoesNotContain(picks, individual => individual.Fitness == 0.4f); - } - - [Fact] - public void DiagnosisFindsTheFirstCheckNoNetworkDoes() - { - CheckDiagnosis diagnosis = CheckDiagnosis.Of(new[] { Scored(PingPong(), 0.5f, 1, 1, 0, 0), Scored(Identity(), 0.4f, 1, 0, 0.5f, 1) }); - - Assert.Equal(new[] { 2 }, diagnosis.Unsolved); - Assert.Equal(2, diagnosis.Frontier); - Assert.StartsWith("No network yet does gap 3 (5)", diagnosis.Describe(new SequenceTask("fib", new[] { 1, 2, 5, 8 }))); - } - - [Fact] - public void ATriggeredPartCountsItsGapsFromTheTrigger() - { - var task = (TriggeredSequenceTask)new SequenceTask("test", new[] { 1, 2, 2, 2, 7 }).Triggered(1)!; - var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(30, 2, OutputTiming.Interval), 1, new Random(1), new EvaluationBudget()); - - FitnessResult result = evaluator.Evaluate(TriggeredTwos()); - Cut cut = ModuleCuts.Whole(TriggeredTwos()); - - Assert.Equal(new[] { 2, 2, 2 }, task.Expected); - Assert.Equal(1, task.InputCount); - Assert.Equal(new[] { 1f, 1f, 1f }, result.Checks); - Assert.True(Solved.Solves(result.Fitness)); - Assert.Equal(3, cut.Body.Neurons.Count); - Assert.Equal(new[] { 0 }, cut.Inputs); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/CompositionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/CompositionTests.cs deleted file mode 100644 index a1ada19..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/CompositionTests.cs +++ /dev/null @@ -1,247 +0,0 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; -using SnpEvolution.Simulation; -using SnpEvolution.Storage; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; - -namespace SnpEvolution.Tests.Evolution -{ - public class CompositionTests - { - private const int MaxGlue = 7; - - // Verified parts of every port kind the first parts use, one with its output role on a port. - private static ModuleLibrary Library() - { - var library = new ModuleLibrary(); - Part increment = ReferenceParts.Increment(); - library.AddPart(Verified(increment with { Network = increment.Network.WithNeuron(2, increment.Network.Neurons[2].WithRoles(true, false)) }), "a test"); - library.AddPart(Verified(ReferenceParts.Register()), "a test"); - library.AddPart(Verified(ReferenceParts.Add()), "a test"); - library.AddPart(Verified(ReferenceParts.Delay(2)), "a test"); - return library; - } - - private static NetworkFactory Factory(int inputCount, Random random) => - new NetworkFactory(new GenomeSpace(InputCount: inputCount, RuleForm: RuleForm.Standard, MaxNeurons: MaxGlue), - new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); - - private static string Json(Network network) => NetworkFiles.ToJson(network); - - [Fact] - [Slow] - public void RandomCompositionsSurviveFlatteningAndRecovery() - { - ModuleLibrary library = Library(); - for (int seed = 0; seed < 200; seed++) - { - var random = new Random(seed); - Composition composition = RandomComposition.Of(library, Factory(seed % 3, random), 1 + seed % 4, random); - - Network flat = composition.Flatten(library); - - Assert.InRange(composition.Glue.Count, 1, seed % 3 + 3); - - Assert.Equal(composition, Composition.Recover(flat, library)); - Assert.Equal(Json(flat), Json(Composition.Recover(flat, library)!.Flatten(library))); - } - } - - [Fact] - [Slow] - public void CompositionsStillRoundTripAfterAnyEditTheSearchMakes() - { - ModuleLibrary library = Library(); - for (int seed = 0; seed < 50; seed++) - { - var random = new Random(seed); - var space = new CompositionSpace(library, Factory(seed % 2, random), new CompositionMix(), random); - WeightedMutation mutation = space.Mutation(1); - Network network = space.NewNetwork(); - for (int step = 0; step < 20; step++) - { - network = mutation.Mutate(network, random); - - Composition composition = Composition.Recover(network, library)!; - Assert.Equal(composition, Composition.Recover(composition.Flatten(library), library)); - Assert.Equal(Json(network), Json(composition.Flatten(library))); - Assert.True(composition.ThroughPorts(library)); - Assert.InRange(composition.Glue.Count, 1, MaxGlue); - } - } - } - - [Fact] - public void TheTwoIncrementChainIsACompositionThatFlattensToTheSameNetwork() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - var first = new Network(increment.Part!.Part.Network.Neurons.Select(neuron => neuron.WithModule(new ModuleTag(increment.Id, 1))).ToList()); - Network chained = ModuleEdits.Insert(first, increment, 2, 24, library, new Random(1)); - PartCopy copy = PartWiring.Copies(chained, library).Single(each => each.Tag.Instance == 1); - - var composition = new Composition( - new[] { new PartInstance(1, increment.Id, 0), new PartInstance(2, increment.Id, 0) }, - Array.Empty(), - new[] { new PortWire(1, "out", 2, "n"), new PortWire(1, "done", 2, "start") }, - Array.Empty(), - new[] { new Endpoint(1, copy["start"]), new Endpoint(1, copy["n"]) }, - Array.Empty()); - - Assert.Equal(Json(chained), Json(composition.Flatten(library))); - Assert.Equal(composition, Composition.Recover(chained, library)); - } - - [Fact] - public void FlatteningTagsEveryPartNeuronWithItsInstance() - { - ModuleLibrary library = Library(); - Composition composition = RandomComposition.Of(library, Factory(1, new Random(3)), 3, new Random(3)); - - Network flat = composition.Flatten(library); - - Assert.Equal(composition.Glue.Count, flat.Neurons.Count(neuron => neuron.Module == null)); - Assert.Equal( - composition.Parts.Select(part => new ModuleTag(part.Module, part.Instance)), - flat.Neurons.Where(neuron => neuron.Module != null).Select(neuron => neuron.Module!).Distinct()); - } - - [Fact] - public void ACopyWhoseInsideChangedIsNotRecovered() - { - ModuleLibrary library = Library(); - Network flat = RandomComposition.Of(library, Factory(0, new Random(2)), 2, new Random(2)).Flatten(library); - int partNeuron = flat.Neurons.ToList().FindIndex(neuron => neuron.Module != null); - - Assert.Null(Composition.Recover(flat.WithNeuron(partNeuron, flat.Neurons[partNeuron].WithInitialSpikes(flat.Neurons[partNeuron].InitialSpikes + 1)), library)); - Assert.Null(Composition.Recover(flat.WithNeuron(partNeuron, flat.Neurons[partNeuron].WithRules(new[] { Rule.Standard("aaa", 3) })), library)); - } - - [Fact] - public void ALinkIntoAPartsOutPortDoesNotPassThroughItsPorts() - { - ModuleLibrary library = Library(); - Composition composition = RandomComposition.Of(library, Factory(0, new Random(4)), 1, new Random(4)); - PartInstance part = composition.Parts[0]; - PartPort outPort = Composition.PartOf(part, library).Part.Ports().First(port => port.Port.Direction == PortDirection.Out); - Composition bypassing = composition with { Links = composition.Links.Append(new Link(Endpoint.GlueAt(1), new Endpoint(part.Instance, outPort.Position))).ToList() }; - - Assert.True(composition.ThroughPorts(library)); - Assert.False(bypassing.ThroughPorts(library)); - Assert.Equal(composition, bypassing.OnlyThroughPorts(library)); - } - - [Fact] - public void TheSearchAdmitsNoMoreGlueThanItsCap() - { - ModuleLibrary library = Library(); - var random = new Random(6); - Composition composition = RandomComposition.Of(library, Factory(0, random), 1, random); - var space = new CompositionSpace(library, Factory(0, random), new CompositionMix(MaxGlue: composition.Glue.Count), random); - Composition overGlued = composition with { Glue = composition.Glue.Append(composition.Glue[0]).ToList() }; - - Assert.NotNull(space.Admit(composition.Flatten(library), dropStrayLinks: false)); - Assert.Null(space.Admit(overGlued.Flatten(library), dropStrayLinks: false)); - } - - // Glue may stop reading a part's port, but a glue edit that starts reading one would change how the part is used. - [Fact] - public void AGlueEditMayNotAddASynapseFromAPartToGlue() - { - ModuleLibrary library = Library(); - var random = new Random(8); - var space = new CompositionSpace(library, Factory(0, random), new CompositionMix(), random); - Composition composition = RandomComposition.Of(library, Factory(0, random), 1, random); - PartInstance part = composition.Parts[0]; - PartPort outPort = Composition.PartOf(part, library).Part.Ports().First(port => port.Port.Direction == PortDirection.Out); - var sender = new Endpoint(part.Instance, outPort.Position); - int unread = Enumerable.Range(1, composition.Glue.Count).First(glue => !composition.Links.Contains(new Link(sender, Endpoint.GlueAt(glue)))); - Network network = composition.Flatten(library); - Network reading = (composition with { Links = composition.Links.Append(new Link(sender, Endpoint.GlueAt(unread))).ToList() }).Flatten(library); - - Assert.Same(network, new CompositionEdit(space, new FixedEdit(reading), glueOnly: true).Mutate(network, random)); - } - - private sealed class FixedEdit : IMutation - { - private readonly Network result; - - public FixedEdit(Network result) => this.result = result; - - public Network Mutate(Network network, Random random) => result; - } - - [Fact] - public void RemovingAPartDropsItsWiresAndKeepsTheRest() - { - ModuleLibrary library = Library(); - for (int seed = 0; seed < 20; seed++) - { - var random = new Random(seed); - Network network = RandomComposition.Of(library, Factory(0, random), 3, random).Flatten(library); - Composition before = Composition.Recover(network, library)!; - - Composition after = Composition.Recover(new RemovePart(library).Mutate(network, random), library)!; - - Assert.True(after.Parts.Count >= before.Parts.Count - 1); - Assert.All(after.Parts, part => Assert.Contains(part, before.Parts)); - Assert.All(after.Wires, wire => Assert.Contains(wire, before.Wires)); - Assert.Equal(before.Glue.Count, after.Glue.Count); - } - } - - [Fact] - [Slow] - public void ABenchmarkRunOfCompositionSearchBuildsFromTheGivenParts() - { - ModuleLibrary library = Library(); - BenchmarkSettings settings = BenchmarkSettings.Default with { Seeds = 1, Parts = library.Parts.Select(module => module.Part!).ToList() }; - EvolutionSearch algorithm = SearchCatalog.CompositionMapElites; - - RunOutcome outcome = Benchmark.RunOnce(algorithm, TaskSuite.Functions.First(task => task.Name == "Compute n + 1"), seed: 1, budget: 400, settings); - - Assert.Contains(outcome.Best!.Genes.Neurons, neuron => neuron.Module != null); - } - - // Every network a composition run keeps is exactly glue and library parts: no part neuron is ever changed. - [Theory] - [Slow] - [InlineData(true)] - [InlineData(false)] - public void ARunInCompositionModeNeverChangesAPartNeuron(bool mapElites) - { - ModuleLibrary library = Library(); - var random = new Random(5); - NetworkFactory factory = Factory(1, random); - var task = FunctionTask.Of("n + 2", n => n + 2, new[] { 1, 2, 3 }); - var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(30, 2, OutputTiming.Interval), 1, random, new EvaluationBudget()); - IGeneticAlgorithm run = (mapElites ? SearchCatalog.CompositionMapElites : SearchCatalog.CompositionTournament) - .Create(new EvolutionContext(16, 0.5f, random, factory.NewNetwork, evaluator, factory, _ => { }, Parts: library)); - - bool sawParts = false; - for (int generation = 0; generation < 30; generation++) - { - run.NextGeneration(); - sawParts |= run.Population.Any(individual => individual.Genes.Neurons.Any(neuron => neuron.Module != null)); - Assert.All(run.Population, individual => - { - Composition? composition = Composition.Recover(individual.Genes, library); - Assert.NotNull(composition); - Assert.True(composition!.ThroughPorts(library)); - Assert.InRange(composition.Glue.Count, 1, MaxGlue); - }); - } - Assert.True(sawParts); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/HardwareProfileTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/HardwareProfileTests.cs deleted file mode 100644 index 5fff3f5..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/HardwareProfileTests.cs +++ /dev/null @@ -1,176 +0,0 @@ -using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Networks; -using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution -{ - public class HardwareProfileTests - { - private static readonly GenomeSpace ProfileSpace = new GenomeSpace(InputCount: 2, RuleForm: RuleForm.Mixed, MaxNeurons: 8, MaxDelay: 3, DuplicateNeurons: true, HardwareProfile: true); - - // A fitness and checks that vary with size, so selection and lexicase have something to choose by. - private static RecordingEvaluator Evaluator() => new RecordingEvaluator(network => 1f / (1 + network.Size % 7), network => new[] { network.Size % 2f }); - - private static NetworkFactory Factory(int seed) - { - var random = new Random(seed); - return new NetworkFactory(ProfileSpace, new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); - } - - [Fact] - public void NetworksMadeUnderTheProfileFitItWithoutBeingConformed() - { - NetworkFactory factory = Factory(1); - - for (int network = 0; network < 300; network++) - { - Network made = factory.NewNetwork(); - Assert.Empty(HardwareProfile.Problems(made)); - } - } - - [Fact] - public void NoNetworkMadeOrMutatedByAnyAlgorithmUnderTheProfileBreaksIt() - { - foreach (EvolutionSearch choice in SearchCatalog.Evolution.Where(search => search is not CompositionSearch)) - { - NetworkFactory factory = Factory(choice.Name.Length); - var evaluator = Evaluator(); - IGeneticAlgorithm algorithm = choice.Create(new EvolutionContext(20, 1f, factory.Random, factory.NewNetwork, evaluator, factory, _ => { }, Lexicase: true)); - - for (int generation = 0; generation < 15; generation++) - { - algorithm.NextGeneration(); - } - - Assert.True(evaluator.Seen.Count > 100, choice.Name); - Network? broken = evaluator.Seen.FirstOrDefault(network => !HardwareProfile.Fits(network)); - Assert.True(broken == null, $"{choice.Name} made a network outside the profile: {string.Join(" ", broken == null ? new string[0] : HardwareProfile.Problems(broken))}"); - } - } - - [Fact] - public void EveryStructuralEditIsPutBackWithinTheProfile() - { - NetworkFactory factory = Factory(7); - var context = new EvolutionContext(10, 1f, factory.Random, factory.NewNetwork, Evaluator(), factory, _ => { }); - foreach (var edit in context.StructuralMutation(1).Edits) - { - var conformed = context.Conformed(edit.Edit); - Network network = factory.NewNetwork(); - for (int step = 0; step < 200; step++) - { - network = conformed.Mutate(network, factory.Random); - Assert.True(HardwareProfile.Fits(network), $"{edit.Name}: {string.Join(" ", HardwareProfile.Problems(network))}"); - } - } - } - - [Fact] - public void PartSearchUnderTheProfileFindsAndShrinksAProfilePart() - { - var settings = new PartSearchSettings(5_000, 1_000, 30, Catalog.StructuralDefault, () => new ExhaustiveCpuEngine(), HardwareProfile: true); - - PartOutcome outcome = PartSearch.Evolve(PartFixtures.DelayContract(2), 1, settings, new EvaluationBudget(), _ => { }); - - Assert.True(outcome.Solved); - Assert.Empty(HardwareProfile.Problems(outcome.Part!.Network)); - } - - [Fact] - public void ShrinkingUnderTheProfileNeverLeavesIt() - { - var random = new Random(4); - var factory = new NetworkFactory(new GenomeSpace(InputCount: 1, MaxDelay: 3, HardwareProfile: true), new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); - (var crossover, var edits) = ShrinkSearch.Operators(factory); - Network network = factory.NewNetwork(); - - for (int step = 0; step < 300; step++) - { - network = edits.Mutate(crossover.Cross(network, factory.NewNetwork(), random), random); - Assert.Empty(HardwareProfile.Problems(network)); - } - } - - [Fact] - public void RejectsAParityConditionAndNamesTheRule() - { - var network = new Network(new[] - { - Neuron(0, new[] { 2 }, HardwareProfile.ThresholdRule(2)), - OutputNeuron(0, new Rule("a(aa)*", 0, true)), - }); - - string problem = Assert.Single(HardwareProfile.Problems(network)); - - Assert.StartsWith("Neuron 2, rule 1 (a(aa)* -> a):", problem); - Assert.Contains("accepts 1, 3, 5, 7, 9, ...", problem); - } - - [Fact] - public void NamesEveryOtherWayOutOfTheProfile() - { - var network = new Network(new[] - { - Neuron(2, new[] { 2 }, Standard("a+", 1, produce: 2), new Rule("aa+", 2, true)), - }); - - IReadOnlyList problems = HardwareProfile.Problems(network); - - Assert.Contains(problems, problem => problem.Contains("starts with 2 spike(s)")); - Assert.Contains(problems, problem => problem.Contains("has 2 rules")); - Assert.Contains(problems, problem => problem.Contains("rule 1 (a+/a -> aa): it consumes 1 spike(s)")); - Assert.Contains(problems, problem => problem.Contains("rule 1 (a+/a -> aa): it sends 2 spikes")); - Assert.Contains(problems, problem => problem.Contains("rule 2 (aa+ -> a;2): its delay of 2 holds the neuron")); - } - - [Theory] - [InlineData("a+", 1)] - [InlineData("aa+", 2)] - [InlineData("a{3,}", 3)] - [InlineData("aaa*", 2)] - [InlineData("(aa|aaa)a*", 2)] - [InlineData("a*", 1)] - [InlineData("a(aa)*", null)] - [InlineData("aaa", null)] - [InlineData("b", null)] - public void ReadsTheThresholdOfAThresholdCondition(string expression, int? threshold) - { - Assert.Equal(threshold, HardwareProfile.Threshold(SpikeCondition.Parse(expression))); - } - - [Fact] - public void ConformsToTheNearestThresholdRule() - { - Rule conformed = HardwareProfile.Conform(Standard("a(aa)+", 2, delay: 2)); - Assert.Equal(("a{3,}", 2, true, true, false), (conformed.Expression, conformed.Delay, conformed.Fire, conformed.Axonal, conformed.IsStandard)); - - Rule forgetting = HardwareProfile.Conform(new Rule("aa", 1, false)); - Assert.Equal(("a{2,}", 0, false, false), (forgetting.Expression, forgetting.Delay, forgetting.Fire, forgetting.Axonal)); - - Neuron neuron = HardwareProfile.Conform(Neuron(3, new[] { 2 }, new Rule("aa+", 0, true), new Rule("a", 0, true))); - Assert.Equal(0, neuron.InitialSpikes); - Assert.Single(neuron.Rules); - } - - [Fact] - public void LeavesAFittingNetworkAsItIs() - { - var network = new Network(new[] { Neuron(0, new[] { 2 }, HardwareProfile.ThresholdRule(1, delay: 2)), OutputNeuron(0, HardwareProfile.ThresholdRule(3, fire: false)) }); - - Assert.Same(network, HardwareProfile.Conform(network)); - } - - [Fact] - public void IsOffUnlessAskedFor() - { - Assert.False(new GenomeSpace().HardwareProfile); - Assert.False(new PartSearchSettings(1, 1, 1, SearchCatalog.Evolution[0], () => null!).HardwareProfile); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/MultiplicationTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/MultiplicationTests.cs deleted file mode 100644 index 4df3704..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/MultiplicationTests.cs +++ /dev/null @@ -1,71 +0,0 @@ -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Simulation; -using Xunit.Abstractions; - -namespace SnpEvolution.Tests.Evolution -{ - // The epic's done-when: n1 x n2 solved by composition search that reuses the add loop promoted from hand-built parts. - public class MultiplicationTests - { - private readonly ITestOutputHelper output; - - public MultiplicationTests(ITestOutputHelper output) => this.output = output; - - private static BenchmarkSettings Settings(ModuleLibrary library) => BenchmarkSettings.Default with - { - Seeds = 1, - PopulationSize = 40, - Repetitions = 2, - Lexicase = true, - CreateEngine = () => new SequentialCpuEngine(), - Parts = library.Parts.Select(module => module.Part!).ToList(), - }; - - private static BenchmarkTask Multiply => TaskSuite.Contracts.Single(task => task.Name == "Contract multiply"); - - private static EvolutionSearch MapElites => SearchCatalog.CompositionMapElites; - - [Fact] - [Slow] - public void CompositionSearchSolvesMultiplicationByReusingThePromotedAddLoop() - { - RunOutcome outcome = Benchmark.RunOnce(MapElites, Multiply, seed: 3, budget: 3_000, Settings(HandBuiltMachines.Library())); - - output.WriteLine($"Solved in {outcome.Evaluations} evaluations; parts in best: {string.Join(", ", outcome.Reuse!.Select(count => $"{count.Contract} {count.Direct} ({count.Nested})"))}"); - Assert.True(outcome.Solved); - Assert.True(outcome.Promoted); - Assert.Equal(1, outcome.Reuse!.Single(count => count.Contract == "add loop").Direct); - Assert.Equal(2, outcome.Reuse!.Single(count => count.Contract == "register").Nested); - } - - [Fact] - public void TheBenchmarkTableReportsWhichPartsTheBestNetworksHold() - { - var outcomes = new[] - { - new RunOutcome("composition", "multiply", 1, false, 100, 0.9f, null, new[] { new PartCount("add loop", 1, 0), new PartCount("register", 0, 2) }), - new RunOutcome("composition", "multiply", 2, false, 300, 0.8f, null, new[] { new PartCount("add", 2, 0) }), - }; - - BenchmarkRow row = Benchmark.Summarise(outcomes).Single(); - string table = Benchmark.FormatTable(new[] { row }); - - Assert.Equal("add 1/2, add loop 1/2", row.ReuseText); - Assert.Equal(2, row.Reuse!.Single(use => use.Contract == "add").MeanCopies); - Assert.Contains("Parts in best (runs)", table); - Assert.Contains("add loop 1/2", Benchmark.FormatCsv(new[] { row })); - } - - [Fact] - public void AFlatRunReportsNoPartColumn() - { - BenchmarkRow row = Benchmark.Summarise(new[] { new RunOutcome("flat", "n + 1", 1, false, 100, 0.5f, null) }).Single(); - - Assert.Null(row.Reuse); - Assert.DoesNotContain("Parts in best", Benchmark.FormatTable(new[] { row })); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/OutputTargetTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/OutputTargetTests.cs deleted file mode 100644 index 52a194f..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/OutputTargetTests.cs +++ /dev/null @@ -1,142 +0,0 @@ -using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution -{ - public class OutputTargetTests - { - private static TrialResult Trains(params int[][] trains) => new TrialResult(Array.Empty(), false, TrialCoverage.Sampled, trains); - - private static FitnessResult Evaluate(ITask task, int maxSteps) => - new FitnessEvaluator(new ParallelCpuEngine(), task, new SimulationOptions(maxSteps, 5, OutputTiming.Interval), 3, new Random(0), new EvaluationBudget()).Evaluate(PingPong()); - - [Theory] - [InlineData("1,1,2,3,5,8,13", new[] { 1, 1, 2, 3, 5, 8, 13 })] - [InlineData("1, 1, 2, 3, 5 ...", new[] { 1, 1, 2, 3, 5 })] - [InlineData("{2 4 6}", new[] { 2, 4, 6 })] - [InlineData("[3,1,4]…", new[] { 3, 1, 4 })] - public void NumbersParseWithAnySeparatorsBracketsAndEllipsis(string input, int[] expected) - { - Assert.True(OutputTarget.TryParse(TargetKind.Sequence, input, out OutputTarget target)); - Assert.Equal(expected, target.Values); - } - - [Theory] - [InlineData("")] - [InlineData("1,0,2")] - [InlineData("1,-2")] - [InlineData("1,x")] - public void NumbersMustBePositiveIntegers(string input) - { - Assert.False(OutputTarget.TryParse(TargetKind.Set, input, out _)); - } - - [Fact] - public void BinaryWordsIgnoreSpacingAndNeedAOne() - { - Assert.True(OutputTarget.TryParse(TargetKind.BinaryWord, "0110 1001_0", out OutputTarget target)); - Assert.Equal(new[] { 0, 1, 1, 0, 1, 0, 0, 1, 0 }, target.Values); - Assert.False(OutputTarget.TryParse(TargetKind.BinaryWord, "0000", out _)); - Assert.False(OutputTarget.TryParse(TargetKind.BinaryWord, "0120", out _)); - } - - [Fact] - public void EachKindMakesItsTask() - { - var fitness = new JaccardFitness(new[] { 1 }); - - Assert.IsType(new OutputTarget(TargetKind.Set, new[] { 1, 1, 2 }).CreateTask(fitness)); - Assert.IsType(new OutputTarget(TargetKind.Sequence, new[] { 1, 1, 2 }).CreateTask(fitness)); - Assert.IsType(new OutputTarget(TargetKind.BinaryWord, new[] { 0, 1 }).CreateTask(fitness)); - } - - [Fact] - public void SequenceScoreRewardsTheRightGapsInOrder() - { - var task = new SequenceTask("fib", new[] { 1, 1, 2, 3 }); - - Assert.Equal(1f, task.Score(new[] { Trains(new[] { 4, 5, 6, 8, 11, 20 }) })); - Assert.Equal(0.5f, task.Score(new[] { Trains(new[] { 0, 1, 2 }) })); - // Half the runs right and half silent: the mean is a half and the worst run nothing, so a quarter. - Assert.Equal(0.25f, task.Score(new[] { Trains(new[] { 0, 1, 2, 4, 7 }, Array.Empty()) })); - } - - [Fact] - public void SequenceScoreStopsAtTheFirstWrongGap() - { - var task = new SequenceTask("fib", new[] { 1, 1, 2, 3 }); - - // Gaps 1,2,2,3: one right, then 2 for 1 earns a quarter, and the 2,3 after the mistake earn nothing. - Assert.Equal(0.3125f, task.Score(new[] { Trains(new[] { 0, 1, 3, 5, 8 }) }), precision: 5); - } - - [Fact] - public void SequenceNicheIsTheRightPrefixByTheLongestGap() - { - var task = new SequenceTask("fib", new[] { 1, 1, 2, 3, 5 }); - - Assert.Equal((3, 3), task.Niche(new[] { Trains(new[] { 0, 1, 2, 4, 9 }) })); - Assert.Equal((0, 0), task.Niche(new[] { Trains(new[] { 4 }) })); - } - - [Fact] - public void PrefixesKeepTheOpeningValues() - { - var sequence = new SequenceTask("fib", new[] { 1, 1, 2, 3, 5 }); - var word = new SpikeWordTask("w", new[] { true, false, true, true }); - - Assert.Equal(new[] { 1, 1, 2 }, ((SequenceTask)sequence.Prefix(3)).Expected); - Assert.Equal(new[] { true, false }, ((SpikeWordTask)word.Prefix(2)).Expected); - Assert.True(sequence.Prefix(3).StepsNeeded < sequence.StepsNeeded); - } - - [Fact] - public void SpikeWordNicheIsTheRightPrefixBySpikeCount() - { - var task = new SpikeWordTask("w", new[] { true, false, true, true }); - - // Fires on steps 0 and 2: right for three steps, with two spikes, as shares of 16 and 8 buckets. - Assert.Equal((12, 4), task.Niche(new[] { Trains(new[] { 0, 2 }) })); - } - - [Fact] - public void SequenceGivesPartialCreditForCloseGaps() - { - var task = new SequenceTask("twos", new[] { 2, 2 }); - - Assert.Equal(0.625f, task.Score(new[] { Trains(new[] { 0, 2, 5 }) }), precision: 5); - } - - [Fact] - public void BinaryWordScoreIsBalancedAccuracy() - { - var task = new SpikeWordTask("w", new[] { false, true, false, true }); - - Assert.Equal(1f, task.Score(new[] { Trains(new[] { 1, 3, 9 }) })); - Assert.Equal(0.5f, task.Score(new[] { Trains(Array.Empty()) })); - Assert.Equal(0.5f, task.Score(new[] { Trains(new[] { 0, 1, 2, 3 }) })); - } - - [Fact] - public void RunsAreLengthenedToFitTheWholeTarget() - { - FitnessResult result = Evaluate(new SequenceTask("twos", Enumerable.Repeat(2, 20).ToList()), maxSteps: 5); - - Assert.Equal(1f, result.Fitness); - Assert.StartsWith("intervals [2,2,2", result.Description); - } - - [Fact] - public void PingPongSpellsAlternatingBits() - { - FitnessResult result = Evaluate(new SpikeWordTask("01", Enumerable.Range(0, 12).Select(step => step % 2 == 1).ToList()), maxSteps: 3); - - Assert.Equal(1f, result.Fitness); - Assert.Equal("spikes 010101010101 / 010101010101", result.Description); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/PartWiringTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/PartWiringTests.cs deleted file mode 100644 index 5e082c8..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/PartWiringTests.cs +++ /dev/null @@ -1,274 +0,0 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution -{ - public class PartWiringTests - { - private const int MaxNeurons = 24; - - // n + 2 on the same values the increment is checked on, with room for two increments in a row. - private static Contract PlusTwo() => new Contract( - "plus two", - Port.In("start", PortKind.Trigger), - new[] { Port.Out("done", PortKind.Trigger) }, - new[] { Port.In("n", PortKind.Count), Port.Out("out", PortKind.Count) }, - FirstParts.Values.Select(n => new ContractCase(new Dictionary { ["n"] = n }, new Dictionary { ["out"] = n + 2 }, "done")).ToList(), - Specifications.LatencyFor(2 * (FirstParts.Larger + 2))); - - // The first copy is the part's own network, so its in-ports are the network's inputs. - private static Network Chained(ModuleLibrary library, Module module, int seed) - { - var first = new Network(module.Part!.Part.Network.Neurons.Select(neuron => neuron.WithModule(new ModuleTag(module.Id, 1))).ToList()); - return ModuleEdits.Insert(first, module, 2, MaxNeurons, library, new Random(seed)); - } - - // The plus-two contract read from the out and done ports of the copy put in last. - private static PartMeasurement MeasurePlusTwo(Network network, ModuleLibrary library) - { - PartCopy last = PartWiring.Copies(network, library).OrderBy(copy => copy.Tag.Instance).Last(); - var binding = new PortBinding(new Dictionary { ["out"] = last["out"], ["done"] = last["done"] }); - return new Verifier(new ContractTask(PlusTwo(), binding), new EvaluationBudget()).Measure(network); - } - - private static NetworkFactory Factory(int maxNeurons, Random random) => - new NetworkFactory(new GenomeSpace(InputCount: 2, RuleForm: RuleForm.Standard, MaxNeurons: maxNeurons), - new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); - - // Evolved parts carry the output role on a port, which the reference increment does not. - private static Part IncrementWithOutput() - { - Part increment = ReferenceParts.Increment(); - return increment with { Network = increment.Network.WithNeuron(2, increment.Network.Neurons[2].WithRoles(true, false)) }; - } - - private static string WireText(Wire wire) => $"{wire.From.Copy.Tag.Instance}.{wire.From.Port.Name}>{wire.To.Copy.Tag.Instance}.{wire.To.Port.Name}"; - - // The task's input feeds the part's in-port and its out-port feeds the task's, rather than any glue neuron at random. - [Fact] - public void AnInsertedPartIsWiredToTheTasksOwnPorts() - { - var library = new ModuleLibrary(); - library.AddPart(Verified(ReferenceParts.Register()), "a test"); - var task = new ContractTask(FirstParts.Named("register")); - var glue = new Network(Enumerable.Range(1, 4).Select(position => new Neuron(new[] { Rule.Standard("a", 1) }, 0, Array.Empty(), false, isInput: position <= 2)).ToList()); - var insert = new InsertModule(library, new GenomeSpace(InputCount: 2, RuleForm: RuleForm.Standard, MaxNeurons: MaxNeurons), task.Boundary); - - for (int seed = 0; seed < 10; seed++) - { - Network network = insert.Mutate(glue, new Random(seed)); - PartCopy copy = PartWiring.Copies(network, library).Single(); - - Assert.Contains(copy["n"], network.Neurons[1].Connections); - Assert.Contains(task.Binding["out"], network.Neurons[copy["out"] - 1].Connections); - } - } - - [Fact] - public void PartsShowTheirPortsAsNeuronPositions() - { - Part increment = ReferenceParts.Increment(); - - Assert.Equal(new[] { "start@1", "n@2", "out@3", "done@4" }, increment.Ports().Select(port => $"{port.Port.Name}@{port.Position}")); - } - - [Fact] - public void OnlyPortsOfTheSameKindAndWidthJoinOutToIn() - { - Port start = Port.In("start", PortKind.Trigger), done = Port.Out("done", PortKind.Trigger); - Port countIn = Port.In("n", PortKind.Count), countOut = Port.Out("out", PortKind.Count); - - Assert.True(PartWiring.Fits(done, start)); - Assert.True(PartWiring.Fits(countOut, countIn)); - Assert.False(PartWiring.Fits(start, done)); - Assert.False(PartWiring.Fits(countOut, start)); - Assert.False(PartWiring.Fits(Port.Out("out", PortKind.Interval), countIn)); - Assert.False(PartWiring.Fits(Port.Out("out", PortKind.Binary, 4), Port.In("n", PortKind.Binary, 5))); - Assert.True(PartWiring.Fits(Port.Out("out", PortKind.Binary, 4), Port.In("n", PortKind.Binary, 4))); - } - - [Fact] - public void TwoVerifiedIncrementsWiredByTypeAddTwoWithNoEvolution() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - - for (int seed = 0; seed < 5; seed++) - { - Network network = Chained(library, increment, seed); - - Assert.Equal(new[] { "1.out>2.n", "1.done>2.start" }, PartWiring.Wires(network, library).Select(WireText)); - PartMeasurement measurement = MeasurePlusTwo(network, library); - Assert.True(measurement.Verdict is Verdict.Passed, measurement.Description); - } - } - - [Fact] - public void AThirdCopyIsFedByAnEarlierOneButNeverDoublesUpAnInPort() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - - for (int seed = 0; seed < 10; seed++) - { - Network network = ModuleEdits.Insert(Chained(library, increment, seed), increment, 3, MaxNeurons, library, new Random(seed)); - - IReadOnlyList wires = PartWiring.Wires(network, library); - Assert.Equal(new[] { "n", "start" }, wires.Where(wire => wire.To.Copy.Tag.Instance == 3).Select(wire => wire.To.Port.Name).Order()); - Assert.All(wires.GroupBy(wire => wire.To.Position), group => Assert.Single(group)); - } - } - - [Fact] - public void APartInANetworkWithNoOtherPartIsWiredToUntypedNeurons() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - - Network network = ModuleEdits.Insert(PingPong(), increment, 1, MaxNeurons, library, new Random(1)); - - PartCopy copy = PartWiring.Copies(network, library).Single(); - Assert.Contains(network.Neurons.Take(2), neuron => neuron.Connections.Contains(copy["start"])); - Assert.Contains(network.Neurons.Take(2), neuron => neuron.Connections.Contains(copy["n"])); - Assert.Empty(PartWiring.Wires(network, library)); - } - - [Fact] - public void APartWhoseInsideWasEditedIsNoLongerTyped() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - Network network = Chained(library, increment, 1); - - Network edited = network.WithNeuron(4, network.Neurons[4].WithRules(new[] { Standard("a", 1) })); - - Assert.Single(PartWiring.Copies(edited, library)); - Assert.Empty(PartWiring.Wires(edited, library)); - } - - [Fact] - public void RewiringOnlyEverJoinsFittingPortsAndNeverFeedsAnInput() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - Network chained = Chained(library, increment, 1); - Network network = ModuleEdits.Insert(chained, increment, 3, MaxNeurons, library, new Random(1)); - var rewire = new RewirePort(library); - var seen = new HashSet(); - - for (int seed = 0; seed < 40; seed++) - { - Network rewired = rewire.Mutate(network, new Random(seed)); - - IReadOnlyList copies = PartWiring.Copies(rewired, library); - Assert.Equal(3, copies.Count); - Dictionary ports = copies.SelectMany(copy => copy.Ports).ToDictionary(port => port.Position); - foreach (CopyPort from in ports.Values) - { - foreach (int target in rewired.Neurons[from.Position - 1].Connections.Where(ports.ContainsKey).Where(target => ports[target].Copy != from.Copy)) - { - Assert.True(PartWiring.Fits(from.Port, ports[target].Port), $"{from.Port.Name} > {ports[target].Port.Name}"); - } - } - Assert.All(rewired.Neurons, neuron => Assert.DoesNotContain(neuron.Connections, target => rewired.Neurons[target - 1].IsInput)); - seen.Add(string.Join(" ", PartWiring.Wires(rewired, library).Select(WireText))); - } - Assert.True(seen.Count > 1); - } - - [Fact] - public void AGlueNeuronSitsOnAWireAndThePairStillAddsTwo() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - Network network = Chained(library, increment, 1); - var random = new Random(1); - - Network glued = new AddGlueNeuron(library, Factory(MaxNeurons, random)).Mutate(network, random); - - Assert.Equal(network.Neurons.Count + 1, glued.Neurons.Count); - Assert.Single(PartWiring.Wires(glued, library)); - Assert.Null(glued.Neurons[^1].Module); - PartMeasurement measurement = MeasurePlusTwo(glued, library); - Assert.True(measurement.Verdict is Verdict.Passed, measurement.Description); - Assert.Same(network, new AddGlueNeuron(library, Factory(network.Neurons.Count, random)).Mutate(network, random)); - } - - [Fact] - public void APartIsSwappedForACheaperOneWithTheSameContractAndKeepsItsWires() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(PartFixtures.PaddedIncrement()), "a test"); - Network padded = Chained(library, increment, 1); - library.AddPart(Verified(ReferenceParts.Increment()), "a cheaper test"); - var swap = new SwapPart(library); - - Network once = swap.Mutate(padded, new Random(1)); - Network twice = swap.Mutate(once, new Random(1)); - - Assert.Equal(new[] { 12, 11, 10 }, new[] { padded, once, twice }.Select(network => network.Neurons.Count)); - Assert.Same(twice, swap.Mutate(twice, new Random(1))); - Assert.Equal(new[] { "1.out>2.n", "1.done>2.start" }, PartWiring.Wires(twice, library).Select(WireText)); - Assert.Equal(new[] { true, true }, twice.Neurons.Take(2).Select(neuron => neuron.IsInput)); - PartMeasurement measurement = MeasurePlusTwo(twice, library); - Assert.True(measurement.Verdict is Verdict.Passed, measurement.Description); - // A role on a neuron with no port of the same name would be lost, so that copy is not swapped. - Network outputOnPadding = padded.WithNeuron(11, padded.Neurons[11].WithRoles(true, false)); - Assert.All(Enumerable.Range(0, 10), seed => Assert.Single(swap.Mutate(outputOnPadding, new Random(seed)).Neurons, neuron => neuron.IsOutput)); - } - - [Fact] - public void APortThatTakesTheOutputSendsNowhereAndAnOldOutputInACopyFeedsNoOtherPort() - { - var library = new ModuleLibrary(); - Module increment = library.AddPart(Verified(IncrementWithOutput()), "a test"); - int takeovers = 0; - - for (int seed = 0; seed < 20; seed++) - { - Network chained = Chained(library, increment, seed); - Network hosted = ModuleEdits.Insert(PingPong(), increment, 1, MaxNeurons, library, new Random(seed)); - - PartCopy first = PartWiring.Copies(chained, library).Single(copy => copy.Tag.Instance == 1); - PartCopy second = PartWiring.Copies(chained, library).Single(copy => copy.Tag.Instance == 2); - PartCopy guest = PartWiring.Copies(hosted, library).Single(); - if (chained.Neurons[second["out"] - 1].IsOutput) - { - takeovers++; - Assert.DoesNotContain(second["out"], chained.Neurons[first["out"] - 1].Connections); - } - if (hosted.Neurons[guest["out"] - 1].IsOutput) - { - Assert.Empty(hosted.Neurons[guest["out"] - 1].Connections); - } - } - Assert.InRange(takeovers, 1, 19); - } - - [Fact] - public void PartEditsAreOnlyOfferedWhenTheLibraryHoldsParts() - { - var harvested = new ModuleLibrary(); - harvested.Add(ModuleCuts.Whole(PingPong()), "a test"); - var withParts = new ModuleLibrary(); - withParts.AddPart(Verified(ReferenceParts.Increment()), "a test"); - NetworkFactory factory = Factory(MaxNeurons, new Random(1)); - - IEnumerable PartEdits(ModuleLibrary library) => - WeightedMutation.Structural(1, factory, modules: new ModuleSupport(library)).Edits.Where(edit => edit.Edit is RewirePort or AddGlueNeuron or SwapPart); - - Assert.Empty(PartEdits(harvested)); - Assert.Equal(3, PartEdits(withParts).Count()); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/SearchTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/SearchTests.cs deleted file mode 100644 index 48f9da4..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/SearchTests.cs +++ /dev/null @@ -1,268 +0,0 @@ -using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; -using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution -{ - public class SearchTests - { - private static NetworkFactory Factory(Random random) => - new NetworkFactory(new GenomeSpace(RuleForm: RuleForm.Standard), new ExpressionGenerator(ExpressionGenerator.SimpleTemplates, 4, random), random); - - private static GeneticAlgorithm Generational(int size, Random random, Func create, IPopulationEvaluator evaluator, IMutation mutation) => - new GeneticAlgorithm(size, random, create, evaluator, new GeneticOperators(new TournamentSelection(3), new NeuronCrossover(), mutation), 1, _ => { }); - - [Fact] - public void FindsTheTypoInAFibonacciTarget() - { - SuspectedTypo? typo = TargetPatterns.Find(new[] { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 114, 233, 377, 610, 987 }); - - Assert.NotNull(typo); - Assert.Equal((11, 114, 144), (typo!.Index, typo.Found, typo.Expected)); - } - - [Theory] - [InlineData(new[] { 2, 4, 6, 9, 10 }, 3, 8)] - [InlineData(new[] { 1, 2, 4, 8, 15, 32 }, 4, 16)] - [InlineData(new[] { 7, 1, 2, 3, 5, 8 }, 0, 1)] - public void FindsASingleValueThatBreaksAPattern(int[] values, int index, int expected) - { - SuspectedTypo? typo = TargetPatterns.Find(values); - - Assert.Equal((index, expected), (typo!.Index, typo.Expected)); - } - - [Theory] - [InlineData(new[] { 1, 1, 2, 3, 5, 8, 13 })] - [InlineData(new[] { 3, 1, 4, 1, 5, 9 })] - [InlineData(new[] { 1, 1, 2, 4 })] - public void LeavesTargetsWithoutAClearTypoAlone(int[] values) - { - Assert.Null(TargetPatterns.Find(values)); - } - - [Fact] - public void CurriculumStagesEndWithTheWholeTarget() - { - Assert.Equal(new[] { 3, 4, 5, 6 }, new CurriculumPlan(3, 1).Lengths(6)); - Assert.Equal(new[] { 8, 12, 15 }, new CurriculumPlan(8, 4).Lengths(15)); - Assert.Equal(new[] { 4 }, new CurriculumPlan(10, 2).Lengths(4)); - } - - [Fact] - public void IterativeEvolutionSolvesEachStageInTurn() - { - var random = new Random(1); - var task = new SequenceTask("twos", Enumerable.Repeat(2, 6).ToList()); - var seen = new List(); - var iterative = new IterativeEvolution(task, new[] { 2, 4, 6 }, - stageTask => - { - seen.Add(((SequenceTask)stageTask).Expected.Count); - return new FitnessEvaluator(new SequentialCpuEngine(), stageTask, new SimulationOptions(5, 3, OutputTiming.Interval), 2, random, new EvaluationBudget()); - }, - evaluator => Generational(6, random, PingPong, evaluator, WeightedMutation.Structural(0, Factory(random))), - _ => { }); - - for (int generation = 0; generation < 10 && !iterative.IsComplete; generation++) - { - iterative.NextGeneration(); - } - - Assert.True(iterative.IsComplete); - Assert.Equal(new[] { 2, 4, 6 }, seen); - Assert.Equal(new[] { 2, 4, 6 }, iterative.Stages.Select(stage => stage.Length)); - Assert.All(iterative.Stages, stage => Assert.True(stage.Solved)); - Assert.Equal(4, iterative.Generation); - } - - [Fact] - public void ALuckyScoreDoesNotSolveAStageAndIsReplacedByItsFailedRetest() - { - var lucky = new Individual(AlwaysOutputsOne()); - lucky.Record(new FitnessResult(1f, new[] { 1 })); - var algorithm = new ModularEvolutionTests.StubAlgorithm(); - algorithm.Individuals.Add(lucky); - var iterative = new IterativeEvolution(new SequenceTask("twos", Enumerable.Repeat(2, 4).ToList()), new[] { 2, 4 }, - stageTask => new FitnessEvaluator(new SequentialCpuEngine(), stageTask, new SimulationOptions(5, 3, OutputTiming.Interval), 2, new Random(0), new EvaluationBudget()), - _ => algorithm, - _ => { }); - - iterative.NextGeneration(); - - Assert.False(iterative.Stages.Single().Solved); - Assert.True(lucky.Fitness < 1f); - } - - [Fact] - public void StagnationEscalatesThenRestartsAndCalmsDownOnANewTask() - { - var random = new Random(2); - NetworkFactory factory = Factory(random); - var pressure = new MutationPressure(); - var evaluator = new RecordingEvaluator(_ => 0.5f); - var recovery = new StagnationRecovery( - Generational(8, random, factory.NewNetwork, evaluator, WeightedMutation.Structural(0.5f, factory, pressure)), - new StagnationPolicy(Patience: 2, ImmigrantFraction: 0.25, MaxExtraEdits: 2), 8, pressure, - factory.NewNetwork, WeightedMutation.Structural(1, factory), random, _ => { }); - - recovery.NextGeneration(); - for (int generation = 0; generation < 4; generation++) - { - recovery.NextGeneration(); - } - Assert.Equal((2, 0, 2), (recovery.Escalations, recovery.Restarts, pressure.ExtraEdits)); - - recovery.NextGeneration(); - recovery.NextGeneration(); - Assert.Equal((1, 0), (recovery.Restarts, pressure.ExtraEdits)); - Assert.Equal(8, recovery.Population.Count); - - recovery.NextGeneration(); - recovery.NextGeneration(); - Assert.Equal(1, pressure.ExtraEdits); - recovery.Rescore(); - Assert.Equal(0, pressure.ExtraEdits); - } - - [Fact] - public void PressureMutatesEveryChild() - { - var random = new Random(3); - NetworkFactory factory = Factory(random); - Network network = factory.NewNetwork(); - - Assert.Same(network, WeightedMutation.Structural(0, factory).Mutate(network, random)); - Assert.NotSame(network, WeightedMutation.Structural(0, factory, new MutationPressure { ExtraEdits = 3 }).Mutate(network, random)); - } - - [Fact] - public void GenerationalImmigrantsReplaceTheWeakestButNotTheElite() - { - var random = new Random(4); - NetworkFactory factory = Factory(random); - GeneticAlgorithm algorithm = Generational(6, random, factory.NewNetwork, new RecordingEvaluator(network => 1f / network.Neurons.Count), WeightedMutation.Structural(1, factory)); - algorithm.NextGeneration(); - Network elite = algorithm.Population[0].Genes; - Network[] newcomers = { NeverOutputs(), NeverOutputs(), NeverOutputs() }; - - algorithm.Immigrate(newcomers); - - Assert.Equal(6, algorithm.Population.Count); - Assert.Same(elite, algorithm.Population[0].Genes); - Assert.Equal(newcomers, algorithm.Population.Skip(3).Select(individual => individual.Genes)); - } - - [Fact] - public void AsManyImmigrantsAsThePopulationStillLeaveTheEliteInPlace() - { - var random = new Random(4); - NetworkFactory factory = Factory(random); - var evaluator = new RecordingEvaluator(network => 1f / network.Neurons.Count); - var algorithms = new IGeneticAlgorithm[] - { - Generational(6, random, factory.NewNetwork, evaluator, WeightedMutation.Structural(1, factory)), - new SpeciatedAlgorithm(6, random, factory.NewNetwork, evaluator, new NeuronCrossover(), WeightedMutation.Structural(1, factory)), - }; - foreach (IGeneticAlgorithm algorithm in algorithms) - { - algorithm.NextGeneration(); - Network elite = algorithm.Population[0].Genes; - - algorithm.Immigrate(Enumerable.Range(0, 6).Select(_ => NeverOutputs()).ToList()); - - Assert.Same(elite, algorithm.Population[0].Genes); - Assert.Equal(6, algorithm.Population.Count); - } - } - - [Fact] - public void NewcomersAreMadeOnlyFromScoredHosts() - { - var scored = new Individual(TestNetworks.Identity()); - scored.Record(new FitnessResult(-1, Array.Empty())); - var unscored = new Individual(NeverOutputs()); - - List made = Immigrants.FromBest(new[] { unscored, scored }, hosts: 1, wanted: 1, attempts: 1, host => host); - - Assert.Same(scored.Genes, Assert.Single(made)); - } - - [Fact] - public void AnEvolutionSearchStartsFromItsSeed() - { - var random = new Random(4); - NetworkFactory factory = Factory(random); - var scoring = new NetworkScoring(() => new SequentialCpuEngine(), new SimulationOptions(20, 1, OutputTiming.Interval), 1); - var setup = new NetworkSetup(4, 0.5f, factory, () => throw new InvalidOperationException("The seed should be used."), scoring); - var seed = new Individual(TestNetworks.Identity()); - - SearchOutcome outcome = SearchCatalog.StructuralDefault.Run( - new SearchRequest(FunctionTask.Of("n", n => n, new[] { 1, 2 }), new EvaluationBudget(), random, _ => { }) { Seeds = new[] { seed }, MaxGenerations = 1, Networks = setup }); - - Assert.Same(seed.Genes, outcome.Best!.Genes); - } - - [Fact] - public void ArchiveAndStrategyImmigrantsJoinTheNextBatch() - { - var random = new Random(5); - NetworkFactory factory = Factory(random); - var evaluator = new RecordingEvaluator(network => 1f / network.Neurons.Count); - var algorithms = new IGeneticAlgorithm[] - { - new MapElites(6, random, factory.NewNetwork, evaluator, new NeuronCrossover(), WeightedMutation.Structural(1, factory)), - new MuPlusLambdaStrategy(2, 6, random, factory.NewNetwork, evaluator, WeightedMutation.Structural(1, factory)), - }; - foreach (IGeneticAlgorithm algorithm in algorithms) - { - algorithm.NextGeneration(); - Network newcomer = NeverOutputs(); - algorithm.Immigrate(new[] { newcomer }); - evaluator.Seen.Clear(); - - algorithm.NextGeneration(); - - Assert.Contains(newcomer, evaluator.Seen); - Assert.Equal(6, evaluator.Seen.Count); - } - } - - [Fact] - public void RescoringTheArchiveUsesTheNewTask() - { - var random = new Random(6); - NetworkFactory factory = Factory(random); - var evaluator = new RecordingEvaluator(_ => 0.9f); - var elites = new MapElites(8, random, factory.NewNetwork, evaluator, new NeuronCrossover(), WeightedMutation.Structural(1, factory)); - elites.NextGeneration(); - int cells = elites.Population.Count; - - evaluator.Fitness = _ => 0.2f; - elites.Rescore(); - - Assert.Equal(0.2f, elites.Best!.Fitness); - Assert.Equal(cells, elites.Population.Count); - Assert.All(elites.Population, elite => Assert.Equal(0.2f, elite.Fitness)); - } - - [Fact] - public void BehaviourNichesReplaceSizeCellsWhenTheTaskHasThem() - { - var individual = new Individual(Identity()); - individual.Record(new FitnessResult(0.5f, Array.Empty())); - Assert.Equal((false, 2, 2), MapElites.CellOf(individual)); - - individual.Record(new FitnessResult(0.5f, Array.Empty(), Niche: (3, 4))); - Assert.Equal((true, 3, 4), MapElites.CellOf(individual)); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/StructuralOperatorTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/StructuralOperatorTests.cs deleted file mode 100644 index b68195f..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/StructuralOperatorTests.cs +++ /dev/null @@ -1,154 +0,0 @@ -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Networks; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution -{ - public class StructuralOperatorTests - { - private static NetworkFactory Factory(int seed, int inputCount = 1, RuleForm form = RuleForm.Mixed) => - new NetworkFactory(new GenomeSpace(InputCount: inputCount, RuleForm: form, MaxNeurons: 6, DuplicateNeurons: true), - new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, new Random(seed)), new Random(seed)); - - private static void AssertWellFormed(Network network, GenomeSpace space) - { - Assert.Single(network.Neurons, neuron => neuron.IsOutput); - Assert.Equal(space.InputCount, network.Neurons.Count(neuron => neuron.IsInput)); - Assert.InRange(network.Neurons.Count, space.SmallestNetwork, space.MaxNeurons); - for (int position = 1; position <= network.Neurons.Count; position++) - { - Neuron neuron = network.Neurons[position - 1]; - Assert.NotEmpty(neuron.Rules); - Assert.InRange(neuron.Rules.Count, 1, space.MaxRulesPerNeuron); - Assert.DoesNotContain(position, neuron.Connections); - Assert.All(neuron.Connections, target => Assert.InRange(target, 1, network.Neurons.Count)); - Assert.Equal(neuron.Connections.Distinct().OrderBy(target => target), neuron.Connections); - Assert.All(neuron.Rules.Where(rule => rule.IsStandard), rule => Assert.True(rule.Consume >= 1 && rule.Produce >= 1)); - Assert.True(neuron.InitialSpikes >= 0); - Assert.True(!neuron.IsInput || neuron.InitialSpikes == 0); - } - } - - [Fact] - public void RandomNetworksAreWellFormed() - { - for (int seed = 0; seed < 100; seed++) - { - NetworkFactory factory = Factory(seed, inputCount: seed % 3); - AssertWellFormed(factory.NewNetwork(), factory.Space); - } - } - - [Fact] - public void EveryEditKeepsNetworksWellFormed() - { - for (int seed = 0; seed < 30; seed++) - { - NetworkFactory factory = Factory(seed, inputCount: seed % 3); - WeightedMutation mutation = WeightedMutation.Structural(1, factory); - var crossover = new NeuronCrossover(); - var random = new Random(seed); - Network network = factory.NewNetwork(); - for (int step = 0; step < 200; step++) - { - network = random.Next(5) == 0 - ? crossover.Cross(network, factory.NewNetwork(), random) - : mutation.Edits[random.Next(mutation.Edits.Count)].Edit.Mutate(network, random); - AssertWellFormed(network, factory.Space); - } - } - } - - [Fact] - public void DuplicateNeuronCopiesANeuronAndItsIncomingSynapses() - { - NetworkFactory factory = Factory(7, inputCount: 0); - Network network = new Network(new[] - { - Neuron(2, new[] { 2 }, Standard("aa", 2)), - Neuron(0, new[] { 3 }, Standard("a", 1)), - OutputNeuron(0, Standard("a", 1)), - }); - - Network duplicated = Enumerable.Range(0, 20).Select(seed => new DuplicateNeuron(factory.Space).Mutate(network, new Random(seed))) - .First(candidate => candidate.Neurons[^1].Rules[0].Expression == "a" && candidate.Neurons[^1].Connections.SequenceEqual(new[] { 3 })); - - Assert.Equal(4, duplicated.Neurons.Count); - Assert.False(duplicated.Neurons[3].IsOutput); - Assert.Equal(new[] { 2, 4 }, duplicated.Neurons[0].Connections); - AssertWellFormed(duplicated, factory.Space with { InputCount = 0 }); - } - - [Fact] - public void DuplicationIsOnlyOfferedWhenAllowed() - { - NetworkFactory allowed = Factory(1); - var notAllowed = new NetworkFactory(allowed.Space with { DuplicateNeurons = false }, new ExpressionGenerator(ExpressionGenerator.SimpleTemplates, 4, new Random(1)), new Random(1)); - - Assert.Contains(WeightedMutation.Structural(1, allowed).Edits, edit => edit.Edit is DuplicateNeuron); - Assert.DoesNotContain(WeightedMutation.Structural(1, notAllowed).Edits, edit => edit.Edit is DuplicateNeuron); - } - - [Fact] - public void StandardSpaceOnlyMakesStandardRules() - { - NetworkFactory factory = Factory(1, form: RuleForm.Standard); - - Assert.All(Enumerable.Range(0, 50).Select(_ => factory.NewRule()), rule => Assert.True(rule.IsStandard)); - } - - [Fact] - public void NudgeExpressionChangesOneRunOfSpikesByOne() - { - Network network = new Network(new[] { OutputNeuron(0, new Rule("aa(aaa)*", 0, true)) }); - var seen = new HashSet(); - - for (int seed = 0; seed < 40; seed++) - { - seen.Add(new NudgeExpression().Mutate(network, new Random(seed)).Neurons[0].Rules[0].Expression); - } - - Assert.Equal(new HashSet { "a(aaa)*", "aaa(aaa)*", "aa(aa)*", "aa(aaaa)*" }, seen); - } - - [Fact] - public void SplitSynapseInsertsARelayInPlaceOfTheSynapse() - { - NetworkFactory factory = Factory(0, inputCount: 0, form: RuleForm.Standard); - - Network split = new SplitSynapse(factory).Mutate(Identity(), new Random(0)); - - Assert.Equal(3, split.Neurons.Count); - Assert.Equal(new[] { 3 }, split.Neurons[0].Connections); - Assert.Equal(new[] { 2 }, split.Neurons[2].Connections); - Assert.Equal("a -> a", NetworkNotation.Rule(split.Neurons[2].Rules[0])); - } - - [Fact] - public void RemoveNeuronRenumbersTheRemainingSynapses() - { - var network = new Network(new[] - { - Neuron(1, new[] { 2, 3 }, new Rule("a", 0, true)), - Neuron(1, new[] { 3 }, new Rule("a", 0, true)), - OutputNeuron(0, new Rule("a", 0, true)), - }); - - Network removed = NetworkEdits.RemoveNeuron(network, 1); - - Assert.Equal(2, removed.Neurons.Count); - Assert.Equal(new[] { 2 }, removed.Neurons[0].Connections); - } - - [Fact] - public void WeightedMutationRespectsItsRate() - { - Network network = Identity(); - var mutation = new WeightedMutation(0, new[] { new WeightedEdit("nudge", new NudgeExpression(), 1) }); - - Assert.Same(network, mutation.Mutate(network, new Random(0))); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/TaskTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Evolution/TaskTests.cs deleted file mode 100644 index 0c518bb..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/TaskTests.cs +++ /dev/null @@ -1,115 +0,0 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; -using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution -{ - public class TaskTests - { - private static readonly SimulationOptions Options = new SimulationOptions(MaxSteps: 40, Repetitions: 20, OutputTiming.Interval); - - private static FitnessResult Evaluate(ITask task, Network network) => - new FitnessEvaluator(new ExhaustiveCpuEngine(), task, Options, solvedRetestCount: 3, new Random(0), new EvaluationBudget()).Evaluate(network); - - private static TrialResult Outputs(params int[] outputs) => new TrialResult(outputs, false, TrialCoverage.Exact); - - private static TrialResult Halts(bool halts) => new TrialResult(Array.Empty(), halts, TrialCoverage.Exact); - - [Fact] - public void IdentityNetworkSolvesTheIdentityFunctionExactly() - { - FitnessResult result = Evaluate(FunctionTask.Of("n", n => n, Enumerable.Range(1, 6)), Identity()); - - Assert.Equal(1f, result.Fitness); - Assert.True(result.Exact); - Assert.Contains("f(3)={3}/3", result.Description); - } - - [Fact] - public void IdentityNetworkIsCloseButWrongForTheSuccessor() - { - float fitness = Evaluate(FunctionTask.Of("n + 1", n => n + 1, Enumerable.Range(1, 6)), Identity()).Fitness; - - Assert.Equal(0.25f, fitness, precision: 5); - } - - [Fact] - public void FunctionScoreGivesFullCreditOnlyForTheRightOutput() - { - var task = new FunctionTask("f", new[] { new FunctionExample(new[] { 2 }, 4), new FunctionExample(new[] { 3 }, 6) }); - - Assert.Equal(1f, task.Score(new[] { Outputs(4), Outputs(6) })); - Assert.Equal(0.5f, task.Score(new[] { Outputs(4), Outputs() })); - Assert.Equal(0.78125f, task.Score(new[] { Outputs(4, 4), Outputs(6, 9) }), precision: 5); - } - - [Fact] - public void TwoArgumentFunctionsNeedTwoInputs() - { - FunctionTask task = FunctionTask.Of("add", (first, second) => first + second, new[] { (1, 2) }); - - Assert.Equal(2, task.InputCount); - Assert.Equal(new[] { 0, 2 }, task.Cases[0].Input.StepsPerInput[1]); - } - - [Fact] - public void AcceptorScoreIsBalancedAccuracy() - { - AcceptorTask task = AcceptorTask.Of("even", n => n % 2 == 0, new[] { 1, 2, 3, 4, 5, 6 }); - - Assert.Equal(1f, task.Score(new[] { false, true, false, true, false, true }.Select(Halts).ToList())); - Assert.Equal(0.5f, task.Score(Enumerable.Repeat(Halts(true), 6).ToList())); - Assert.Equal(0.5f, task.Score(Enumerable.Repeat(Halts(false), 6).ToList())); - Assert.Equal(0.5f, AcceptorTask.Of("big", n => n >= 4, new[] { 1, 2, 3, 4 }).Score(Enumerable.Repeat(Halts(true), 4).ToList())); - Assert.Equal(Readout.Halting, task.Cases[0].Readout); - } - - [Fact] - public void AcceptorThatAlwaysHaltsAcceptsEverything() - { - var network = new Network(new[] { InputNeuron(Array.Empty(), StandardForget("a", 1)), OutputNeuron(0, Standard("a", 1)) }); - - FitnessResult result = Evaluate(AcceptorTask.Of("big", n => n >= 3, Enumerable.Range(1, 4)), network); - - Assert.Equal(0.5f, result.Fitness); - Assert.Equal("accepts {1,2,3,4}", result.Description); - } - - [Fact] - public void GeneratorTaskScoresWithItsFitnessFunction() - { - var task = new GeneratorTask("{1}", new[] { 1 }, new JaccardFitness(new[] { 1 })); - - FitnessResult result = Evaluate(task, AlwaysOutputsOne()); - - Assert.Equal(1f, result.Fitness); - Assert.Equal("{1}", result.Description); - Assert.Equal(new[] { 0f, 1f, 0f }, new GeneratorTask("{1,2,3}", new[] { 1, 2, 3 }, new JaccardFitness(new[] { 1, 2, 3 })).Checks(new[] { 2 })); - } - - [Fact] - public void ExactResultIsReliablySolvedWithoutRetesting() - { - var budget = new EvaluationBudget(); - var evaluator = new FitnessEvaluator(new ExhaustiveCpuEngine(), FunctionTask.Of("n", n => n, new[] { 1, 2 }), Options, solvedRetestCount: 5, new Random(0), budget); - - Assert.True(evaluator.ConfirmSolved(Identity()).Solved); - Assert.Equal(1, budget.Networks); - } - - [Fact] - public void SuiteTasksAreAllUsableWithTheirOwnSettings() - { - foreach (BenchmarkTask task in TaskSuite.All) - { - Assert.NotEmpty(task.Task.Cases); - FitnessResult result = Evaluate(task.Task, Identity()); - Assert.InRange(result.Fitness, 0, 1); - } - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Export/NirExporterTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Export/NirExporterTests.cs index cc1eaba..11b3842 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Export/NirExporterTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Export/NirExporterTests.cs @@ -1,11 +1,11 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; using SnpEvolution.Export; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Export { @@ -19,15 +19,9 @@ private static Part ProfilePart(string file) private static string Check(NirDescription description) { - string folder = Path.Combine(Path.GetTempPath(), "snp-nir-" + Guid.NewGuid().ToString("N")); - try - { - return NirExporter.WriteAndCheck(description, folder); - } - finally - { - Directory.Delete(folder, recursive: true); - } + using var temp = new TempFolder("snp-nir"); + string folder = temp.Path; + return NirExporter.WriteAndCheck(description, folder); } [Fact] @@ -58,7 +52,7 @@ public void RandomProfileNetworksMatchOurTracesInNorse() { var random = new Random(3); var space = new GenomeSpace(InputCount: 2, MaxNeurons: 7, MaxDelay: 3, HardwareProfile: true); - var factory = new NetworkFactory(space, new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); + var factory = Factories.Networks(space, random); var input = new InputSpikes(new IReadOnlyList[] { new[] { 0, 1, 1, 2, 5, 6, 6, 6 }, new[] { 0, 3, 4, 4, 9 } }); int firings = 0, delays = 0, forgetting = 0; for (int network = 0; network < 4; network++) diff --git a/SNP_Evolution/SNP_Evolution.Tests/Export/UppaalExporterTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Export/UppaalExporterTests.cs index b8b23e0..a691b98 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Export/UppaalExporterTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Export/UppaalExporterTests.cs @@ -1,10 +1,9 @@ using System.Xml; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; using SnpEvolution.Export; -using SnpEvolution.Networks; -using SnpEvolution.Tests.Evolution; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; namespace SnpEvolution.Tests.Export { @@ -12,19 +11,11 @@ public class UppaalExporterTests { private static Part LibraryPart(string folder, string file) => RepositoryFiles.ReadPart(folder, file).Part; - private static string Folder() => Path.Combine(Path.GetTempPath(), "snp-uppaal-" + Guid.NewGuid().ToString("N")); - private static IReadOnlyList Check(Part part) { - string folder = Folder(); - try - { - return Verifyta.Check(UppaalExporter.Export(part), folder); - } - finally - { - Directory.Delete(folder, recursive: true); - } + using var temp = new TempFolder("snp-uppaal"); + string folder = temp.Path; + return Verifyta.Check(UppaalExporter.Export(part), folder); } private static bool Holds(Part part, string query) @@ -123,7 +114,7 @@ public void TheHandBuiltRegisterMeetsEveryContractRuleOnEveryCaseInUppaal() [VerifytaFact] public void UppaalAgreesWithTheBoundedCheckOnARegisterFailingAtTwenty() { - Part broken = BoundedCheckTests.RegisterFailingAtTwenty(); + Part broken = PartFixtures.RegisterFailingAtTwenty(); Contract upToTwenty = broken.Contract with { Cases = Enumerable.Range(0, 21).Select(n => Specification.For(broken.Contract)!.Expected(new Dictionary { ["n"] = n })).ToList(), MaxLatency = Specifications.LatencyFor(20) }; Assert.False(Holds(broken with { Contract = upToTwenty }, "done once, the right one, with the right outputs")); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Export/VerilogExporterTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Export/VerilogExporterTests.cs index 4dad73d..4307c26 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Export/VerilogExporterTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Export/VerilogExporterTests.cs @@ -1,12 +1,11 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; using SnpEvolution.Export; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Export { @@ -22,15 +21,9 @@ private static (string Simulated, string Expected) CoSimulate(Part part) var cases = SpikeTrace.Cases(part.Contract); List inputs = cases.Select(@case => @case.Input).ToList(); List steps = cases.Select(@case => @case.Steps).ToList(); - string folder = Path.Combine(Path.GetTempPath(), "snp-verilog-" + Guid.NewGuid().ToString("N")); - try - { - return (Iverilog.Simulate(design, VerilogTestbench.For(design, inputs, steps), folder), VerilogTestbench.ExpectedOutput(part.Network, inputs, steps)); - } - finally - { - Directory.Delete(folder, recursive: true); - } + using var temp = new TempFolder("snp-verilog"); + string folder = temp.Path; + return (Iverilog.Simulate(design, VerilogTestbench.For(design, inputs, steps), folder), VerilogTestbench.ExpectedOutput(part.Network, inputs, steps)); } public static TheoryData Parts() => new TheoryData { "library delay 2", "register", "add", "increment" }; @@ -57,7 +50,7 @@ public void PartsMatchOurEngineStepForStepOnEveryContractCase() { (string simulated, string expected) = CoSimulate(Named(name)); - Assert.True(expected == simulated, $"{name} differs from our engine first at: {FirstDifference(expected, simulated)}"); + Assert.True(expected == simulated, $"{name} differs from our engine. {TextDifference.FirstDifference(expected, simulated)}"); Assert.EndsWith("overflow 0\n", simulated); } } @@ -69,7 +62,7 @@ public void RandomDeterministicNetworksMatchOurEngineStepForStep() { var random = new Random(5); var space = new GenomeSpace(InputCount: 2, RuleForm: RuleForm.Mixed, MaxNeurons: 6, MaxDelay: 3, MaxRulesPerNeuron: 2); - var factory = new NetworkFactory(space, new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); + var factory = Factories.Networks(space, random); var input = new InputSpikes(new IReadOnlyList[] { new[] { 0, 1, 1, 4, 9 }, new[] { 2, 3, 7 } }); int exported = 0; var delays = new HashSet(); @@ -84,17 +77,11 @@ public void RandomDeterministicNetworksMatchOurEngineStepForStep() delays.UnionWith(network.Neurons.SelectMany(neuron => neuron.Rules).Where(rule => rule.Delay > 0).Select(rule => rule.DelayKind)); long mostHeld = SpikeTrace.Run(network, input, 40).MostHeld; VerilogDesign design = VerilogExporter.Export(network, $"random {attempt}", NetworkPort.Plain(network), mostHeld); - string folder = Path.Combine(Path.GetTempPath(), "snp-verilog-" + Guid.NewGuid().ToString("N")); - try - { - string simulated = Iverilog.Simulate(design, VerilogTestbench.For(design, new[] { input }, new[] { 40 }), folder); - string expected = VerilogTestbench.ExpectedOutput(network, new[] { input }, new[] { 40 }); - Assert.True(expected == simulated, $"{NetworkNotation.Format(network)}\ndiffers first at: {FirstDifference(expected, simulated)}"); - } - finally - { - Directory.Delete(folder, recursive: true); - } + using var temp = new TempFolder("snp-verilog"); + string folder = temp.Path; + string simulated = Iverilog.Simulate(design, VerilogTestbench.For(design, new[] { input }, new[] { 40 }), folder); + string expected = VerilogTestbench.ExpectedOutput(network, new[] { input }, new[] { 40 }); + Assert.True(expected == simulated, $"{NetworkNotation.Format(network)}\ndiffers from our engine. {TextDifference.FirstDifference(expected, simulated)}"); } Assert.Equal(25, exported); Assert.Equal(new[] { DelayKind.Closing, DelayKind.Holding, DelayKind.Axonal }, delays.OrderBy(kind => kind)); @@ -142,12 +129,5 @@ public void ExportsTheSameTextEveryTime() private static Network AxonalCopy(Network network, Random random) => new Network(network.Neurons.Select(neuron => neuron.WithRules(neuron.Rules.Select(rule => rule.Delay > 0 && random.Next(2) == 0 ? rule.WithAxonal(true) : rule))).ToList()); - private static string FirstDifference(string expected, string simulated) - { - string[] want = expected.Split('\n'); - string[] got = simulated.Split('\n'); - int line = Enumerable.Range(0, Math.Min(want.Length, got.Length)).FirstOrDefault(index => want[index] != got[index], Math.Min(want.Length, got.Length)); - return $"line {line + 1}: expected '{want.ElementAtOrDefault(line)}', got '{got.ElementAtOrDefault(line)}'"; - } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/BenchmarkFixtures.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/BenchmarkFixtures.cs new file mode 100644 index 0000000..5584788 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/BenchmarkFixtures.cs @@ -0,0 +1,10 @@ +using SnpEvolution.Search.Benchmarking; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class BenchmarkFixtures + { + // One seed per algorithm and task, so a benchmark test stays quick. + public static readonly BenchmarkSettings OneSeed = BenchmarkSettings.Default with { Seeds = 1 }; + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/CompositionFixtures.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/CompositionFixtures.cs new file mode 100644 index 0000000..7dff62e --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/CompositionFixtures.cs @@ -0,0 +1,28 @@ +using SnpEvolution.Search.Genome; +using SnpEvolution.Specs.Parts; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class CompositionFixtures + { + // The most glue neurons a composition test lets the search add. + public const int MaxGlue = 7; + + // Verified parts of every port kind the first parts use, one with its output role on a port. + public static ModuleLibrary Library() + { + var library = new ModuleLibrary(); + Part increment = ReferenceParts.Increment(); + library.AddPart(Verified(increment with { Network = increment.Network.WithNeuron(2, increment.Network.Neurons[2].WithRoles(true, false)) }), "a test"); + library.AddPart(Verified(ReferenceParts.Register()), "a test"); + library.AddPart(Verified(ReferenceParts.Add()), "a test"); + library.AddPart(Verified(ReferenceParts.Delay(2)), "a test"); + return library; + } + + // Makes the glue neurons between parts: standard rules, up to MaxGlue of them. + public static NetworkFactory GlueFactory(int inputCount, Random random) => + Factories.Networks(new GenomeSpace(InputCount: inputCount, RuleForm: RuleForm.Standard, MaxNeurons: MaxGlue), random); + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/ConsoleCapture.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/ConsoleCapture.cs new file mode 100644 index 0000000..ee609e8 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/ConsoleCapture.cs @@ -0,0 +1,35 @@ +namespace SnpEvolution.Tests.Fixtures +{ + // Points the process-wide console at strings until disposed, so a command's output stays out of the test log. Every + // test running at the time shares the console, so a test using this belongs to ProcessStateCollection. + internal sealed class ConsoleCapture : IDisposable + { + private readonly TextWriter output = Console.Out; + private readonly TextWriter error = Console.Error; + private readonly StringWriter printed = new StringWriter { NewLine = "\n" }; + private readonly StringWriter errors = new StringWriter { NewLine = "\n" }; + + public ConsoleCapture() + { + Console.SetOut(printed); + Console.SetError(errors); + } + + public string Printed => printed.ToString(); + + public string Errors => errors.ToString(); + + public void Dispose() + { + Console.SetOut(output); + Console.SetError(error); + } + } + + // Tests that swap the console, the working directory or the culture, which the whole process shares, run alone. + [CollectionDefinition(Name, DisableParallelization = true)] + public sealed class ProcessStateCollection + { + public const string Name = "Process-wide state"; + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/DelegateEvaluator.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/DelegateEvaluator.cs new file mode 100644 index 0000000..16a1e84 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/DelegateEvaluator.cs @@ -0,0 +1,15 @@ +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; + +namespace SnpEvolution.Tests.Fixtures +{ + // Scores each network with the function it is given. + internal sealed class DelegateEvaluator : IPopulationEvaluator + { + private readonly Func evaluate; + + public DelegateEvaluator(Func evaluate) => this.evaluate = evaluate; + + public IReadOnlyList EvaluateAll(IReadOnlyList networks) => networks.Select(evaluate).ToList(); + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Engines.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Engines.cs new file mode 100644 index 0000000..b275a65 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Engines.cs @@ -0,0 +1,12 @@ +using SnpEvolution.Simulation; + +namespace SnpEvolution.Tests.Fixtures +{ + // The CPU engines, for theories that run the same trials on each. + public static class Engines + { + public static TheoryData Sampling => new TheoryData { new SequentialCpuEngine(), new ParallelCpuEngine() }; + + public static TheoryData Cpu => new TheoryData { new SequentialCpuEngine(), new ParallelCpuEngine(), new ExhaustiveCpuEngine() }; + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Factories.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Factories.cs new file mode 100644 index 0000000..6062200 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Factories.cs @@ -0,0 +1,16 @@ +using SnpEvolution.Search.Genome; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class Factories + { + // Makes networks in the space with rule expressions of up to length symbols, drawing both from random unless + // the expressions are given their own source. + public static NetworkFactory Networks(GenomeSpace space, Random random, IReadOnlyList? templates = null, int length = 4, Random? expressions = null) => + new NetworkFactory(space, new ExpressionGenerator(templates ?? ExpressionGenerator.ExperimentalTemplates, length, expressions ?? random), random); + + // Standard rules with the one simple template, the space the algorithm tests evolve in. + public static NetworkFactory StandardRules(Random random) => + Networks(new GenomeSpace(RuleForm: RuleForm.Standard), random, ExpressionGenerator.SimpleTemplates); + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/GeneticAlgorithms.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/GeneticAlgorithms.cs new file mode 100644 index 0000000..4dd2026 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/GeneticAlgorithms.cs @@ -0,0 +1,14 @@ +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Operators; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class GeneticAlgorithms + { + // A generational algorithm with tournament selection and neuron crossover, so a test picks only the mutation. + public static GeneticAlgorithm Generational(int size, Random random, Func create, IPopulationEvaluator evaluator, IMutation mutation) => + new GeneticAlgorithm(size, random, create, evaluator, new GeneticOperators(new TournamentSelection(3), new NeuronCrossover(), mutation), 1, _ => { }); + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleFixtures.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/ModuleFixtures.cs similarity index 60% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleFixtures.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/ModuleFixtures.cs index f18db61..232b75b 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleFixtures.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/ModuleFixtures.cs @@ -1,15 +1,15 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; -using static SnpEvolution.Tests.TestNetworks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; +using static SnpEvolution.Tests.Fixtures.TestNetworks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Fixtures { internal static class ModuleFixtures { - internal static Individual Scored(Network network, float fitness, params float[] checks) + public static Individual Scored(Network network, float fitness, params float[] checks) { var individual = new Individual(network); individual.Record(new FitnessResult(fitness, Array.Empty(), Checks: checks)); @@ -17,7 +17,7 @@ internal static Individual Scored(Network network, float fitness, params float[] } // n1 -> n2 -> n3 (out), with n2 waiting a step. - internal static Network Chain() => new Network(new[] + public static Network Chain() => new Network(new[] { Neuron(1, new[] { 2 }, Standard("a", 1)), Neuron(0, new[] { 3 }, Standard("a", 1, delay: 1)), @@ -25,17 +25,17 @@ internal static Individual Scored(Network network, float fitness, params float[] }); // A part checked against its contract on the exhaustive engine and recorded as a library part would be. - internal static LibraryPart Verified(Part part) + public static LibraryPart Verified(Part part) { PartMeasurement measurement = Verifier.Measure(part, new EvaluationBudget()); Assert.True(measurement.Verdict is Verdict.Passed, measurement.Description); - return measurement.ToLibraryPart(part, new PartOrigin(1, "a test", 0)); + return measurement.ToLibraryPart(part, PartFixtures.ATest); } - internal static Module ModuleOf(ModuleLibrary library, Network network) => library.Add(ModuleCuts.Whole(network), "a test")!; + public static Module ModuleOf(ModuleLibrary library, Network network) => library.Add(ModuleCuts.Whole(network), "a test")!; // The input passes the trigger on, n2 waits a step, and the output and n4 then pass a spike back and forth. - internal static Network TriggeredTwos() => new Network(new[] + public static Network TriggeredTwos() => new Network(new[] { InputNeuron(new[] { 2 }, Standard("a", 1)), Neuron(0, new[] { 3 }, Standard("a", 1)), diff --git a/SNP_Evolution/SNP_Evolution.Tests/PartFixtures.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/PartFixtures.cs similarity index 61% rename from SNP_Evolution/SNP_Evolution.Tests/PartFixtures.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/PartFixtures.cs index 7ec4dac..a2e74a0 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/PartFixtures.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/PartFixtures.cs @@ -1,12 +1,23 @@ -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; -namespace SnpEvolution.Tests +namespace SnpEvolution.Tests.Fixtures { // Hand-built parts broken or padded in exactly one way, and contracts with fewer cases than the catalogue's. internal static class PartFixtures { + public static readonly PartOrigin ATest = new PartOrigin(1, "a test", 0); + + // The hand-built parts by their place in HandBuiltParts.All(), for a theory over each. + public static TheoryData HandBuilt => new TheoryData(Enumerable.Range(0, HandBuiltParts.All().Count)); + + // Measured on the exhaustive engine and recorded as a library part would be. + public static LibraryPart Measured(Part part, PartOrigin? origin = null) => + Verifier.Measure(part, new EvaluationBudget()).ToLibraryPart(part, origin ?? ATest); + public static Contract DelayContract(int k) => CatalogueEntry.OneCase(Specifications.Delay(k)).Contract; // The register's cases up to largest, with a latency of largest + 2, so a test runs quickly. @@ -43,5 +54,16 @@ public static Part PaddedIncrement() Part increment = ReferenceParts.Increment(); return increment with { Network = new Network(increment.Network.Neurons.Append(new Neuron(new[] { Rule.Standard("a", 1) }, 0, new int[0], false)).ToList()) }; } + + // The hand-built register, except that a store holding 41 spikes (n = 20 and start) sends two spikes instead of one. + public static Part RegisterFailingAtTwenty() + { + Part register = HandBuiltParts.All().Single(part => part.Contract.Name == "register"); + const int Store = 4; + List neurons = register.Network.Neurons.ToList(); + neurons[Store] = new Neuron( + new[] { Rule.Standard("a(aa){1,19}|a(aa){21,}", 2), Rule.Standard("a{41}", 2, 2), Rule.Standard("a", 1, 2) }, 0, neurons[Store].Connections, false); + return register with { Network = new Network(neurons) }; + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/PartWiringFixtures.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/PartWiringFixtures.cs new file mode 100644 index 0000000..eb47516 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/PartWiringFixtures.cs @@ -0,0 +1,41 @@ +using SnpEvolution.Model; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class PartWiringFixtures + { + // Room for two increments and the glue between them. + public const int MaxNeurons = 24; + + // n + 2 on the same values the increment is checked on, with room for two increments in a row. + public static Contract PlusTwo() => new Contract( + "plus two", + Port.In("start", PortKind.Trigger), + new[] { Port.Out("done", PortKind.Trigger) }, + new[] { Port.In("n", PortKind.Count), Port.Out("out", PortKind.Count) }, + FirstParts.Values.Select(n => new ContractCase(new Dictionary { ["n"] = n }, new Dictionary { ["out"] = n + 2 }, "done")).ToList(), + Specifications.LatencyFor(2 * (FirstParts.Larger + 2))); + + // The first copy is the part's own network, so its in-ports are the network's inputs. + public static Network Chained(ModuleLibrary library, Module module, int seed) + { + var first = new Network(module.Part!.Part.Network.Neurons.Select(neuron => neuron.WithModule(new ModuleTag(module.Id, 1))).ToList()); + return ModuleEdits.Insert(first, module, 2, MaxNeurons, library, new Random(seed)); + } + + // The plus-two contract read from the out and done ports of the copy put in last. + public static PartMeasurement MeasurePlusTwo(Network network, ModuleLibrary library) + { + PartCopy last = PartWiring.Copies(network, library).OrderBy(copy => copy.Tag.Instance).Last(); + var binding = new PortBinding(new Dictionary { ["out"] = last["out"], ["done"] = last["done"] }); + return new Verifier(new ContractTask(PlusTwo(), binding), new EvaluationBudget()).Measure(network); + } + + public static string WireText(Wire wire) => $"{wire.From.Copy.Tag.Instance}.{wire.From.Port.Name}>{wire.To.Copy.Tag.Instance}.{wire.To.Port.Name}"; + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/RecordingEvaluator.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/RecordingEvaluator.cs similarity index 88% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/RecordingEvaluator.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/RecordingEvaluator.cs index 5b784d3..fbad056 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/RecordingEvaluator.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/RecordingEvaluator.cs @@ -1,7 +1,7 @@ -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Fixtures { // Scores networks with a given function and keeps every network an algorithm asked to have scored. internal sealed class RecordingEvaluator : IPopulationEvaluator diff --git a/SNP_Evolution/SNP_Evolution.Tests/RepositoryFiles.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/RepositoryFiles.cs similarity index 76% rename from SNP_Evolution/SNP_Evolution.Tests/RepositoryFiles.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/RepositoryFiles.cs index b6ec4e5..867be2c 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/RepositoryFiles.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/RepositoryFiles.cs @@ -1,8 +1,8 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Parts; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; -namespace SnpEvolution.Tests +namespace SnpEvolution.Tests.Fixtures { // Found from the test assembly, not the working directory, which some tests change. internal static class RepositoryFiles @@ -10,6 +10,9 @@ internal static class RepositoryFiles public static string Root { get; } = RunFolders.RepositoryRootAbove(AppContext.BaseDirectory) ?? throw new DirectoryNotFoundException($"No repository above {AppContext.BaseDirectory}."); + // The folder holding the solution and its projects. + public static string Solution => Path.Combine(Root, "SNP_Evolution"); + public static string PartFile(string folder, string file) => Path.Combine(Root, folder, file); public static LibraryPart ReadPart(string folder, string file) => PartLibraryFiles.Read(File.ReadAllText(PartFile(folder, file)), file); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Runs.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Runs.cs new file mode 100644 index 0000000..49d0067 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Runs.cs @@ -0,0 +1,35 @@ +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class Runs + { + // Long and repeated enough for the task tests' small networks to show every behaviour. + public static readonly SimulationOptions TaskOptions = new SimulationOptions(MaxSteps: 40, Repetitions: 20, OutputTiming.Interval); + + // One step at a time, choosing rules from a fixed seed. + public static NetworkSimulation Simulate(Network network, InputSpikes? input = null, OutputTiming timing = OutputTiming.Interval) => + new NetworkSimulation(CompiledNetwork.Of(network), new Random(0), input ?? InputSpikes.None, timing); + + // Scores the network on the task as a run would: on the exhaustive engine for one step and five repetitions + // unless told otherwise. + public static FitnessResult Evaluate(ITask task, Network network, ISimulationEngine? engine = null, SimulationOptions? options = null, int solvedRetestCount = 1) => + new FitnessEvaluator(engine ?? new ExhaustiveCpuEngine(), task, options ?? new SimulationOptions(1, 5, OutputTiming.Interval), solvedRetestCount, new Random(0), new EvaluationBudget()) + .Evaluate(network); + + // Scores sequence targets on the sampling engine, as the compile and shrink runs do. + public static FitnessEvaluator SequenceEvaluator(IReadOnlyList values) => + new FitnessEvaluator(new SequentialCpuEngine(), new SequenceTask("target", values), new SimulationOptions(50, 2, OutputTiming.Interval), 1, new Random(1), new EvaluationBudget()); + + // Every number the network outputs, over all its computations. + public static IReadOnlyList Generated(Network network, int maxSteps = 400) => + new ExhaustiveCpuEngine().CollectOutputs(new[] { network }, new SimulationOptions(maxSteps, 20, OutputTiming.Interval), new Random(1))[0]; + + // Rewards networks for having exactly three neurons, which every algorithm should manage. + public static float ThreeNeurons(Network network) => 1f / (1 + Math.Abs(network.Neurons.Count - 3)); + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Sequences.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Sequences.cs new file mode 100644 index 0000000..c217689 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/Sequences.cs @@ -0,0 +1,7 @@ +namespace SnpEvolution.Tests.Fixtures +{ + internal static class Sequences + { + public static readonly int[] Fibonacci = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584 }; + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/SlowAttribute.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/SlowAttribute.cs similarity index 86% rename from SNP_Evolution/SNP_Evolution.Tests/SlowAttribute.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/SlowAttribute.cs index 9289ca3..6c0fc4e 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/SlowAttribute.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/SlowAttribute.cs @@ -1,7 +1,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace SnpEvolution.Tests +namespace SnpEvolution.Tests.Fixtures { // Marks a test that runs a search, a bounded check with a time limit or an external tool, so the fast set // (dotnet test --filter Speed!=slow) can leave it out. @@ -14,7 +14,7 @@ public sealed class SlowAttribute : Attribute, ITraitAttribute // Gives Speed=slow to every attribute that names it, including the facts that run an external tool. public sealed class SpeedDiscoverer : ITraitDiscoverer { - public const string TypeName = "SnpEvolution.Tests.SpeedDiscoverer"; + public const string TypeName = "SnpEvolution.Tests.Fixtures." + nameof(SpeedDiscoverer); public const string AssemblyName = "SNP_Evolution.Tests"; public IEnumerable> GetTraits(IAttributeInfo traitAttribute) diff --git a/SNP_Evolution/SNP_Evolution.Tests/SlowAttributeTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/SlowAttributeTests.cs similarity index 75% rename from SNP_Evolution/SNP_Evolution.Tests/SlowAttributeTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/SlowAttributeTests.cs index 933fc82..8698a78 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/SlowAttributeTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/SlowAttributeTests.cs @@ -1,10 +1,9 @@ -using SnpEvolution.Tests.Evolution; using SnpEvolution.Tests.Export; -using SnpEvolution.Tests.Simulation; -using Xunit.Abstractions; +using SnpEvolution.Tests.Search.Proposals; +using SnpEvolution.Tests.Simulation.Metal; using Xunit.Sdk; -namespace SnpEvolution.Tests +namespace SnpEvolution.Tests.Fixtures { public class SlowAttributeTests { @@ -12,10 +11,10 @@ public class SlowAttributeTests [Fact] public void SlowAndToolTestsCarryTheSlowTraitAndOthersDoNot() { - Assert.Equal(new[] { "slow" }, SpeedOf(typeof(ProposalTests), nameof(ProposalTests.ARandomSequenceAsksForNothing))); + Assert.Equal(new[] { "slow" }, SpeedOf(typeof(RecurrenceProposerTests), nameof(RecurrenceProposerTests.ARandomSequenceAsksForNothing))); Assert.Equal(new[] { "slow" }, SpeedOf(typeof(VerilogExporterTests), nameof(VerilogExporterTests.PartsMatchOurEngineStepForStepOnEveryContractCase))); Assert.Equal(new[] { "slow" }, SpeedOf(typeof(MetalEngineTests), nameof(MetalEngineTests.MatchesTheCpuOnSmallSingleComputationNetworks))); - Assert.Empty(SpeedOf(typeof(ProposalTests), nameof(ProposalTests.PowersOfTwoAskForARegisterAndADouble))); + Assert.Empty(SpeedOf(typeof(RecurrenceProposerTests), nameof(RecurrenceProposerTests.PowersOfTwoAskForARegisterAndADouble))); } private static List SpeedOf(Type testClass, string method) diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/StubAlgorithm.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/StubAlgorithm.cs new file mode 100644 index 0000000..df2cdb5 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/StubAlgorithm.cs @@ -0,0 +1,30 @@ +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; + +namespace SnpEvolution.Tests.Fixtures +{ + // A fixed population that records what it is given, so the modular loop can be watched without evolving. + internal sealed class StubAlgorithm : IGeneticAlgorithm + { + public List Individuals { get; } = new List(); + + public List Immigrants { get; } = new List(); + + public int Rescores { get; private set; } + + public IReadOnlyList Population => Individuals; + + public int Generation { get; private set; } = 1; + + public Individual? Best => Individuals.Count == 0 ? null : Ranking.Rank(Individuals)[0]; + + public IReadOnlyList> FitnessHistory => Array.Empty>(); + + public void NextGeneration() => Generation++; + + public void Immigrate(IReadOnlyList newcomers) => Immigrants.AddRange(newcomers); + + public void Rescore() => Rescores++; + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TempFolder.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TempFolder.cs new file mode 100644 index 0000000..044187d --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TempFolder.cs @@ -0,0 +1,28 @@ +namespace SnpEvolution.Tests.Fixtures +{ + // A folder of its own under the system's temporary folder, named but not made, and deleted with everything in it + // when disposed. + internal sealed class TempFolder : IDisposable + { + public TempFolder(string name = "snp") + { + Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"{name}-{Guid.NewGuid():N}"); + } + + public string Path { get; } + + public TempFolder Made() + { + Directory.CreateDirectory(Path); + return this; + } + + public void Dispose() + { + if (Directory.Exists(Path)) + { + Directory.Delete(Path, recursive: true); + } + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/TestNetworks.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TestNetworks.cs similarity index 91% rename from SNP_Evolution/SNP_Evolution.Tests/TestNetworks.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/TestNetworks.cs index 6e49fac..fa0551c 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/TestNetworks.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TestNetworks.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Tests +namespace SnpEvolution.Tests.Fixtures { internal static class TestNetworks { @@ -44,5 +44,7 @@ public static Rule Standard(string expression, long consume, int produce = 1, in { OutputNeuron(1, new Rule("a", 0, false)), }); + + public static Rule Axonal(string expression, int delay) => new Rule(expression, delay, true, axonal: true); } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TextDifference.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TextDifference.cs new file mode 100644 index 0000000..53755af --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TextDifference.cs @@ -0,0 +1,22 @@ +namespace SnpEvolution.Tests.Fixtures +{ + internal static class TextDifference + { + // The first line where two texts differ, or null when they are the same. + public static string? FirstDifference(string expected, string actual) + { + string[] expectedLines = expected.Split('\n'); + string[] actualLines = actual.Split('\n'); + for (int line = 0; line < Math.Max(expectedLines.Length, actualLines.Length); line++) + { + string? was = line < expectedLines.Length ? expectedLines[line] : null; + string? now = line < actualLines.Length ? actualLines[line] : null; + if (was != now) + { + return $"First difference at line {line + 1}:\n expected: {was ?? "(end of file)"}\n actual: {now ?? "(end of file)"}"; + } + } + return null; + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TextDifferenceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TextDifferenceTests.cs new file mode 100644 index 0000000..bdb0904 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TextDifferenceTests.cs @@ -0,0 +1,13 @@ +namespace SnpEvolution.Tests.Fixtures +{ + public class TextDifferenceTests + { + [Fact] + public void NamesTheFirstLineThatDiffersIncludingAMissingLastLine() + { + Assert.Null(TextDifference.FirstDifference("a\nb\n", "a\nb\n")); + Assert.Equal("First difference at line 2:\n expected: b\n actual: c", TextDifference.FirstDifference("a\nb\n", "a\nc\n")); + Assert.Equal("First difference at line 3:\n expected: c\n actual: (end of file)", TextDifference.FirstDifference("a\nb\nc", "a\nb")); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Export/ToolFactAttributes.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/ToolFactAttributes.cs similarity index 73% rename from SNP_Evolution/SNP_Evolution.Tests/Export/ToolFactAttributes.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/ToolFactAttributes.cs index 368f7bd..4d28b7d 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Export/ToolFactAttributes.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/ToolFactAttributes.cs @@ -1,7 +1,8 @@ using SnpEvolution.Export; +using SnpEvolution.Simulation.Metal; using Xunit.Sdk; -namespace SnpEvolution.Tests.Export +namespace SnpEvolution.Tests.Fixtures { // A test that runs Icarus Verilog, skipped with the reason when it is not installed; slow, as every tool test is. [TraitDiscoverer(SpeedDiscoverer.TypeName, SpeedDiscoverer.AssemblyName)] @@ -41,4 +42,17 @@ public VerifytaFactAttribute() } } } + + // A test that runs on the GPU, skipped on machines without a Metal GPU; slow. + [TraitDiscoverer(SpeedDiscoverer.TypeName, SpeedDiscoverer.AssemblyName)] + public sealed class MetalFactAttribute : FactAttribute, ITraitAttribute + { + public MetalFactAttribute() + { + if (!MetalEngine.IsAvailable) + { + Skip = "This machine has no Metal GPU."; + } + } + } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TrialResults.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TrialResults.cs new file mode 100644 index 0000000..1891ac8 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/TrialResults.cs @@ -0,0 +1,10 @@ +using SnpEvolution.Simulation; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class TrialResults + { + // A sampled result that recorded these spike trains and no outputs. + public static TrialResult Trains(params int[][] trains) => new TrialResult(Array.Empty(), false, TrialCoverage.Sampled, trains); + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/WellFormedNetworks.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/WellFormedNetworks.cs new file mode 100644 index 0000000..4a5fa19 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/WellFormedNetworks.cs @@ -0,0 +1,32 @@ +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class WellFormedNetworks + { + // Up to six neurons of any rule form, with duplicated neurons allowed, so every structural edit has room. + public static NetworkFactory SmallFactory(int seed, int inputCount = 1, RuleForm form = RuleForm.Mixed) => + Factories.Networks(new GenomeSpace(InputCount: inputCount, RuleForm: form, MaxNeurons: 6, DuplicateNeurons: true), new Random(seed), expressions: new Random(seed)); + + // What every network a factory or an edit makes must satisfy. + public static void AssertWellFormed(Network network, GenomeSpace space) + { + Assert.Single(network.Neurons, neuron => neuron.IsOutput); + Assert.Equal(space.InputCount, network.Neurons.Count(neuron => neuron.IsInput)); + Assert.InRange(network.Neurons.Count, space.SmallestNetwork, space.MaxNeurons); + for (int position = 1; position <= network.Neurons.Count; position++) + { + Neuron neuron = network.Neurons[position - 1]; + Assert.NotEmpty(neuron.Rules); + Assert.InRange(neuron.Rules.Count, 1, space.MaxRulesPerNeuron); + Assert.DoesNotContain(position, neuron.Connections); + Assert.All(neuron.Connections, target => Assert.InRange(target, 1, network.Neurons.Count)); + Assert.Equal(neuron.Connections.Distinct().OrderBy(target => target), neuron.Connections); + Assert.All(neuron.Rules.Where(rule => rule.IsStandard), rule => Assert.True(rule.Consume >= 1 && rule.Produce >= 1)); + Assert.True(neuron.InitialSpikes >= 0); + Assert.True(!neuron.IsInput || neuron.InitialSpikes == 0); + } + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandGoldenTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandGoldenTests.cs index 8c5443f..179d178 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandGoldenTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandGoldenTests.cs @@ -1,10 +1,9 @@ -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; +using SnpEvolution.Specs.Parts; namespace SnpEvolution.Tests.Golden { // Populations stay under the GPU's batch threshold, so a Mac with Metal runs these on the CPU exactly as Linux does. - [Collection(GoldenCollection.Name)] + [Collection(ProcessStateCollection.Name)] public class CommandGoldenTests { [Fact] diff --git a/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandRun.cs b/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandRun.cs index 41848f7..af3271e 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandRun.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandRun.cs @@ -2,20 +2,20 @@ using System.Text; using System.Text.RegularExpressions; using SnpEvolution.Cli; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; namespace SnpEvolution.Tests.Golden { - // Swaps the process-wide console, working directory and culture, so tests using it belong to GoldenCollection. + // Swaps the process-wide console, working directory and culture, so tests using it belong to ProcessStateCollection. internal sealed partial class CommandRun : IDisposable { // Pictures of a network, which only lay out what the .json and .txt files already hold. private static readonly string[] Drawings = { ".html", ".svg" }; - public string Folder { get; } = Path.Combine(Path.GetTempPath(), "snp-golden-" + Guid.NewGuid().ToString("N")); + private readonly TempFolder folder = new TempFolder("snp-golden"); + + public string Folder => folder.Path; // The .git marker makes the folder the root the commands find runs/ and parts/ from, wherever the temp folder lies. public CommandRun() @@ -40,7 +40,7 @@ public void CopyFile(string repositoryFile) // Saves a hand-built part as a library file, measured the way evolve-parts measures the parts it saves. public void Save(Part part, string file) => - File.WriteAllText(Path.Combine(Folder, file), PartLibraryFiles.ToJson(Verifier.Measure(part, new EvaluationBudget()).ToLibraryPart(part, new PartOrigin(0, HandBuiltParts.Origin, 0)))); + File.WriteAllText(Path.Combine(Folder, file), PartLibraryFiles.ToJson(PartFixtures.Measured(part, new PartOrigin(0, HandBuiltParts.Origin, 0)))); // The command line, exit code, output and every file the run left in the folder. public string Run(params string[] args) @@ -57,23 +57,18 @@ public string Run(params string[] args) private (int Exit, string Printed, string Errors) RunInFolder(string[] args) { - TextWriter output = Console.Out, error = Console.Error; string directory = Directory.GetCurrentDirectory(); CultureInfo culture = CultureInfo.CurrentCulture; - var printed = new StringWriter { NewLine = "\n" }; - var errors = new StringWriter { NewLine = "\n" }; + using var console = new ConsoleCapture(); try { - Console.SetOut(printed); - Console.SetError(errors); Directory.SetCurrentDirectory(Folder); CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; - return (CommandLine.Run(args), printed.ToString(), errors.ToString()); + int exit = CommandLine.Run(args); + return (exit, console.Printed, console.Errors); } finally { - Console.SetOut(output); - Console.SetError(error); Directory.SetCurrentDirectory(directory); CultureInfo.CurrentCulture = culture; } @@ -105,13 +100,7 @@ private string Normalise(string text) return NirToolsMissing().Replace(text, "$1"); } - public void Dispose() - { - if (Directory.Exists(Folder)) - { - Directory.Delete(Folder, recursive: true); - } - } + public void Dispose() => folder.Dispose(); [GeneratedRegex(@"runs[/\\]\d+")] private static partial Regex RunFolder(); @@ -122,10 +111,4 @@ public void Dispose() [GeneratedRegex(@"(Not converted to NIR or co-simulated: ).*")] private static partial Regex NirToolsMissing(); } - - [CollectionDefinition(Name, DisableParallelization = true)] - public sealed class GoldenCollection - { - public const string Name = "Golden runs"; - } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Golden/ContractGoldenTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Golden/ContractGoldenTests.cs index b824409..59c41d9 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Golden/ContractGoldenTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Golden/ContractGoldenTests.cs @@ -1,9 +1,9 @@ using System.Text; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; namespace SnpEvolution.Tests.Golden diff --git a/SNP_Evolution/SNP_Evolution.Tests/Golden/EngineTraceGoldenTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Golden/EngineTraceGoldenTests.cs index 151b169..2d24458 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Golden/EngineTraceGoldenTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Golden/EngineTraceGoldenTests.cs @@ -1,12 +1,10 @@ using System.Text; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; using SnpEvolution.Simulation.Metal; -using SnpEvolution.Tests.Simulation; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; namespace SnpEvolution.Tests.Golden { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenFile.cs b/SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenFile.cs index 025d4bd..e1ce4b3 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenFile.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenFile.cs @@ -5,7 +5,7 @@ internal static class GoldenFile { private const string UpdateFlag = "SNP_UPDATE_GOLDEN"; - private static string Folder => Path.Combine(RepositoryFiles.Root, "SNP_Evolution", "SNP_Evolution.Tests", "Golden", "Expected"); + private static string Folder => Path.Combine(RepositoryFiles.Solution, "SNP_Evolution.Tests", "Golden", "Expected"); public static void Check(string name, string actual) { @@ -19,27 +19,12 @@ public static void Check(string name, string actual) Assert.True(File.Exists(path), $"There is no golden file {name}.txt; run the tests with {UpdateFlag}=1 to write it."); // Git may check the file out with Windows line endings. string expected = File.ReadAllText(path).ReplaceLineEndings("\n"); - if (FirstDifference(expected, actual) is string difference) + if (TextDifference.FirstDifference(expected, actual) is string difference) { Assert.Fail($"{name}.txt differs from this run. {difference}\n" + $"If the change is intended, rerun with {UpdateFlag}=1 and say why in the pull request."); } } - public static string? FirstDifference(string expected, string actual) - { - string[] expectedLines = expected.Split('\n'); - string[] actualLines = actual.Split('\n'); - for (int line = 0; line < Math.Max(expectedLines.Length, actualLines.Length); line++) - { - string? was = line < expectedLines.Length ? expectedLines[line] : null; - string? now = line < actualLines.Length ? actualLines[line] : null; - if (was != now) - { - return $"First difference at line {line + 1}:\n expected: {was ?? "(end of file)"}\n actual: {now ?? "(end of file)"}"; - } - } - return null; - } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenFileTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenFileTests.cs deleted file mode 100644 index df0801a..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenFileTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace SnpEvolution.Tests.Golden -{ - public class GoldenFileTests - { - [Fact] - public void NamesTheFirstLineThatDiffersIncludingAMissingLastLine() - { - Assert.Null(GoldenFile.FirstDifference("a\nb\n", "a\nb\n")); - Assert.Equal("First difference at line 2:\n expected: b\n actual: c", GoldenFile.FirstDifference("a\nb\n", "a\nc\n")); - Assert.Equal("First difference at line 3:\n expected: c\n actual: (end of file)", GoldenFile.FirstDifference("a\nb\nc", "a\nb")); - } - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Layering/InterfaceLayerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Layering/InterfaceLayerTests.cs deleted file mode 100644 index 3627fc5..0000000 --- a/SNP_Evolution/SNP_Evolution.Tests/Layering/InterfaceLayerTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace SnpEvolution.Tests.Layering -{ - // The menu and the command line sit on top: the run services and everything under them work without them, so a - // new interface or a test can call the services directly. - public class InterfaceLayerTests - { - private const string Interface = "SnpEvolution.Cli"; - - // Program starts the menu or the command line, so only it may. - private static readonly string[] Entry = { "SnpEvolution", Interface }; - - [Fact] - public void NothingBelowTheInterfaceDependsOnIt() - { - Dictionary> graph = NamespaceGraph.Build(); - - List dependents = graph.Where(node => !Entry.Contains(node.Key) && node.Value.Contains(Interface)).Select(node => node.Key).ToList(); - - Assert.True(dependents.Count == 0, "These depend on the interface:\n" + string.Join("\n", dependents.Select(from => $" {from}: {NamespaceGraph.Reasons(from, Interface)}"))); - } - - [Fact] - public void TheApplicationLayerIsInTheGraph() => - Assert.Contains("SnpEvolution.Application", NamespaceGraph.Build()[Interface]); - } -} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Layering/Layers.cs b/SNP_Evolution/SNP_Evolution.Tests/Layering/Layers.cs new file mode 100644 index 0000000..cfa18c0 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Layering/Layers.cs @@ -0,0 +1,15 @@ +using System.Reflection; + +namespace SnpEvolution.Tests.Layering +{ + // The program's projects, lowest first. Each is the namespace SnpEvolution. in the folder of that name, and + // may reference only the projects before it. + internal static class Layers + { + public static readonly string[] Projects = { "Model", "Simulation", "Specs", "Compilation", "Search", "Storage", "Export", "Application", "Cli" }; + + public static string Folder(string project) => Path.Combine(RepositoryFiles.Solution, project); + + public static Assembly Assembly(string project) => System.Reflection.Assembly.Load("SnpEvolution." + project); + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Layering/NamespaceCycleTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Layering/NamespaceCycleTests.cs index 88cd6f2..b604b42 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Layering/NamespaceCycleTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Layering/NamespaceCycleTests.cs @@ -1,7 +1,7 @@ namespace SnpEvolution.Tests.Layering { - // The project split later in the milestone needs the namespaces to form layers, so no namespace may depend on - // itself through others. + // The build keeps projects from depending on each other in a cycle, but not the namespaces inside one project, so + // no namespace may depend on itself through others. public class NamespaceCycleTests { [Fact] diff --git a/SNP_Evolution/SNP_Evolution.Tests/Layering/NamespaceGraph.cs b/SNP_Evolution/SNP_Evolution.Tests/Layering/NamespaceGraph.cs index 397c757..fff5f98 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Layering/NamespaceGraph.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Layering/NamespaceGraph.cs @@ -1,7 +1,6 @@ using System.Reflection; using System.Reflection.Emit; using System.Text.RegularExpressions; -using SnpEvolution.Networks; namespace SnpEvolution.Tests.Layering { @@ -89,8 +88,7 @@ private static SortedSet Edges(Dictionary> gra private static IEnumerable<(string From, string To)> UsingLines() { - string folder = Path.Combine(RepositoryFiles.Root, "SNP_Evolution", "SNP_Evolution"); - foreach (string path in Directory.GetFiles(folder, "*.cs", SearchOption.AllDirectories).Where(path => !path.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}"))) + foreach (string path in Layers.Projects.SelectMany(project => Directory.GetFiles(Layers.Folder(project), "*.cs", SearchOption.AllDirectories)).Where(path => !path.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}"))) { string text = File.ReadAllText(path); if (NamespaceLine().Match(text) is not { Success: true } declared) @@ -112,8 +110,8 @@ private static SortedSet Edges(Dictionary> gra // Each type reference across namespaces, with the types at each end, to say why an edge is there. public static IEnumerable<(string From, string To, string FromType, string ToType)> TypeReferencesWithTypes() { - Assembly assembly = typeof(Network).Assembly; - foreach (Type type in assembly.GetTypes()) + Assembly[] assemblies = Layers.Projects.Select(Layers.Assembly).ToArray(); + foreach (Type type in assemblies.SelectMany(assembly => assembly.GetTypes())) { string? from = OwnNamespace(type); if (from == null) @@ -124,7 +122,7 @@ private static SortedSet Edges(Dictionary> gra { foreach (Type part in Parts(referenced)) { - if (part.Assembly == assembly && OwnNamespace(part) is string to && to != from) + if (assemblies.Contains(part.Assembly) && OwnNamespace(part) is string to && to != from) { yield return (from, to, Outermost(type).Name, Outermost(part).Name); } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Layering/ProjectLayerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Layering/ProjectLayerTests.cs new file mode 100644 index 0000000..ad67139 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Layering/ProjectLayerTests.cs @@ -0,0 +1,60 @@ +using System.Xml.Linq; + +namespace SnpEvolution.Tests.Layering +{ + // The build keeps a project from using one it does not reference, so the layers hold if no project references one + // above it, and each project's code sits in its own namespace. + public class ProjectLayerTests + { + public static TheoryData Projects() => new(Layers.Projects); + + [Fact] + public void TheSolutionHoldsTheLayersAndTheTests() + { + XDocument solution = XDocument.Load(Path.Combine(RepositoryFiles.Solution, "SNP_Evolution.slnx")); + + IEnumerable listed = solution.Descendants("Project").Select(project => project.Attribute("Path")!.Value.Split('/')[0]); + + Assert.Equal(Layers.Projects.Append("SNP_Evolution.Tests").Order(), listed.Order()); + } + + [Theory] + [MemberData(nameof(Projects))] + public void NoProjectReferencesOneAboveIt(string project) + { + XDocument file = XDocument.Load(Path.Combine(Layers.Folder(project), project + ".csproj")); + + IEnumerable above = file.Descendants("ProjectReference") + .Select(reference => Path.GetFileNameWithoutExtension(reference.Attribute("Include")!.Value.Replace('\\', '/'))) + .Where(referenced => Array.IndexOf(Layers.Projects, referenced) is int at && (at < 0 || at >= Array.IndexOf(Layers.Projects, project))); + + Assert.Empty(above); + } + + [Theory] + [MemberData(nameof(Projects))] + public void EachProjectsCodeIsInItsOwnNamespace(string project) + { + string own = "SnpEvolution." + project; + + IEnumerable elsewhere = Layers.Assembly(project).GetTypes() + .Where(type => type.DeclaringType == null && type.Namespace != null && type.Namespace.StartsWith("SnpEvolution", StringComparison.Ordinal)) + .Where(type => type.Namespace != own && !type.Namespace!.StartsWith(own + ".", StringComparison.Ordinal)) + .Select(type => type.FullName!); + + Assert.Empty(elsewhere); + } + + [Fact] + public void TheCompiledAssembliesReferenceOnlyLayersBelow() + { + IEnumerable upwards = Layers.Projects.SelectMany((project, at) => Layers.Assembly(project).GetReferencedAssemblies() + .Select(reference => reference.Name!) + .Where(name => name.StartsWith("SnpEvolution.", StringComparison.Ordinal)) + .Where(name => Array.IndexOf(Layers.Projects, name["SnpEvolution.".Length..]) >= at) + .Select(name => $"{project} -> {name}")); + + Assert.Empty(upwards); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Model/HardwareProfileTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Model/HardwareProfileTests.cs new file mode 100644 index 0000000..f8c9a92 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Model/HardwareProfileTests.cs @@ -0,0 +1,77 @@ +using SnpEvolution.Model; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Model +{ + public class HardwareProfileTests + { + [Fact] + public void RejectsAParityConditionAndNamesTheRule() + { + var network = new Network(new[] + { + Neuron(0, new[] { 2 }, HardwareProfile.ThresholdRule(2)), + OutputNeuron(0, new Rule("a(aa)*", 0, true)), + }); + + string problem = Assert.Single(HardwareProfile.Problems(network)); + + Assert.StartsWith("Neuron 2, rule 1 (a(aa)* -> a):", problem); + Assert.Contains("accepts 1, 3, 5, 7, 9, ...", problem); + } + + [Fact] + public void NamesEveryOtherWayOutOfTheProfile() + { + var network = new Network(new[] + { + Neuron(2, new[] { 2 }, Standard("a+", 1, produce: 2), new Rule("aa+", 2, true)), + }); + + IReadOnlyList problems = HardwareProfile.Problems(network); + + Assert.Contains(problems, problem => problem.Contains("starts with 2 spike(s)")); + Assert.Contains(problems, problem => problem.Contains("has 2 rules")); + Assert.Contains(problems, problem => problem.Contains("rule 1 (a+/a -> aa): it consumes 1 spike(s)")); + Assert.Contains(problems, problem => problem.Contains("rule 1 (a+/a -> aa): it sends 2 spikes")); + Assert.Contains(problems, problem => problem.Contains("rule 2 (aa+ -> a;2): its delay of 2 holds the neuron")); + } + + [Theory] + [InlineData("a+", 1)] + [InlineData("aa+", 2)] + [InlineData("a{3,}", 3)] + [InlineData("aaa*", 2)] + [InlineData("(aa|aaa)a*", 2)] + [InlineData("a*", 1)] + [InlineData("a(aa)*", null)] + [InlineData("aaa", null)] + [InlineData("b", null)] + public void ReadsTheThresholdOfAThresholdCondition(string expression, int? threshold) + { + Assert.Equal(threshold, HardwareProfile.Threshold(SpikeCondition.Parse(expression))); + } + + [Fact] + public void ConformsToTheNearestThresholdRule() + { + Rule conformed = HardwareProfile.Conform(Standard("a(aa)+", 2, delay: 2)); + Assert.Equal(("a{3,}", 2, true, true, false), (conformed.Expression, conformed.Delay, conformed.Fire, conformed.Axonal, conformed.IsStandard)); + + Rule forgetting = HardwareProfile.Conform(new Rule("aa", 1, false)); + Assert.Equal(("a{2,}", 0, false, false), (forgetting.Expression, forgetting.Delay, forgetting.Fire, forgetting.Axonal)); + + Neuron neuron = HardwareProfile.Conform(Neuron(3, new[] { 2 }, new Rule("aa+", 0, true), new Rule("a", 0, true))); + Assert.Equal(0, neuron.InitialSpikes); + Assert.Single(neuron.Rules); + } + + [Fact] + public void LeavesAFittingNetworkAsItIs() + { + var network = new Network(new[] { Neuron(0, new[] { 2 }, HardwareProfile.ThresholdRule(1, delay: 2)), OutputNeuron(0, HardwareProfile.ThresholdRule(3, fire: false)) }); + + Assert.Same(network, HardwareProfile.Conform(network)); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkEditsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkEditsTests.cs new file mode 100644 index 0000000..85e77e1 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkEditsTests.cs @@ -0,0 +1,24 @@ +using SnpEvolution.Model; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Model +{ + public class NetworkEditsTests + { + [Fact] + public void RemoveNeuronRenumbersTheRemainingSynapses() + { + var network = new Network(new[] + { + Neuron(1, new[] { 2, 3 }, new Rule("a", 0, true)), + Neuron(1, new[] { 3 }, new Rule("a", 0, true)), + OutputNeuron(0, new Rule("a", 0, true)), + }); + + Network removed = NetworkEdits.RemoveNeuron(network, 1); + + Assert.Equal(2, removed.Neurons.Count); + Assert.Equal(new[] { 2 }, removed.Neurons[0].Connections); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkGraphTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkGraphTests.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkGraphTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Model/NetworkGraphTests.cs index e9667a3..761e99a 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkGraphTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkGraphTests.cs @@ -1,7 +1,7 @@ using System.Xml.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Tests.Networks +namespace SnpEvolution.Tests.Model { public class NetworkGraphTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkNotationTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkNotationTests.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkNotationTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Model/NetworkNotationTests.cs index c8d4db1..491fefd 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkNotationTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkNotationTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Tests.Networks +namespace SnpEvolution.Tests.Model { public class NetworkNotationTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkPageTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkPageTests.cs similarity index 90% rename from SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkPageTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Model/NetworkPageTests.cs index 2dd9407..272ef48 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Networks/NetworkPageTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Model/NetworkPageTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Tests.Networks +namespace SnpEvolution.Tests.Model { public class NetworkPageTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Networks/RuleTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Model/RuleTests.cs similarity index 87% rename from SNP_Evolution/SNP_Evolution.Tests/Networks/RuleTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Model/RuleTests.cs index 689b3dd..fa1dd89 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Networks/RuleTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Model/RuleTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Tests.Networks +namespace SnpEvolution.Tests.Model { public class RuleTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Networks/SpikeConditionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Model/SpikeConditionTests.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution.Tests/Networks/SpikeConditionTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Model/SpikeConditionTests.cs index 289f485..8fb6714 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Networks/SpikeConditionTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Model/SpikeConditionTests.cs @@ -1,7 +1,7 @@ using System.Text.RegularExpressions; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Tests.Networks +namespace SnpEvolution.Tests.Model { public class SpikeConditionTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Networks/StandardRuleTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Model/StandardRuleTests.cs similarity index 76% rename from SNP_Evolution/SNP_Evolution.Tests/Networks/StandardRuleTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Model/StandardRuleTests.cs index 59de67d..be39d80 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Networks/StandardRuleTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Model/StandardRuleTests.cs @@ -1,8 +1,7 @@ -using SnpEvolution.Networks; -using SnpEvolution.Storage; -using static SnpEvolution.Tests.TestNetworks; +using SnpEvolution.Model; +using static SnpEvolution.Tests.Fixtures.TestNetworks; -namespace SnpEvolution.Tests.Networks +namespace SnpEvolution.Tests.Model { public class StandardRuleTests { @@ -75,26 +74,6 @@ public void TableMarksInputNeurons() Assert.StartsWith("n1 (in)", NetworkNotation.Format(Identity()).Split(Environment.NewLine)[1]); } - [Fact] - public void StandardRulesAndInputNeuronsRoundTripThroughJson() - { - var network = new Network(new[] - { - InputNeuron(new[] { 2 }, Standard("a(aa)*", 1, produce: 2, delay: 1)), - OutputNeuron(0, new Rule("a", 0, true)), - }); - - string json = NetworkFiles.ToJson(network); - Network loaded = Assert.IsType(NetworkFiles.FromJson(json)); - - Rule rule = loaded.Neurons[0].Rules[0]; - Assert.Equal((1L, 2, 1), (rule.Consume!.Value, rule.Produce, rule.Delay)); - Assert.True(loaded.Neurons[0].IsInput); - Assert.Null(loaded.Neurons[1].Rules[0].Consume); - Assert.DoesNotContain("\"Size\"", json); - Assert.Equal(json, NetworkFiles.ToJson(loaded)); - } - [Fact] public void SizeCountsNeuronsRulesAndSynapses() { diff --git a/SNP_Evolution/SNP_Evolution.Tests/SNP_Evolution.Tests.csproj b/SNP_Evolution/SNP_Evolution.Tests/SNP_Evolution.Tests.csproj index 09f5c82..175bd50 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/SNP_Evolution.Tests.csproj +++ b/SNP_Evolution/SNP_Evolution.Tests/SNP_Evolution.Tests.csproj @@ -2,6 +2,8 @@ false + SNP_Evolution.Tests + SnpEvolution.Tests @@ -11,11 +13,12 @@ - + + diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/GeneticAlgorithmTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/GeneticAlgorithmTests.cs similarity index 76% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/GeneticAlgorithmTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/GeneticAlgorithmTests.cs index 6d15fbf..2fd5fa6 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/GeneticAlgorithmTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/GeneticAlgorithmTests.cs @@ -1,14 +1,15 @@ -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Search.Algorithms { public class GeneticAlgorithmTests { - private static Network SingleRuleNetwork(string expression) => new Network(new[] + // One output neuron holding a spike, with the one rule. + private static Network LoneOutputNeuron(string expression) => new Network(new[] { TestNetworks.OutputNeuron(1, new Rule(expression, 0, true)), }); @@ -31,19 +32,10 @@ private static GeneticAlgorithm Create( new RuleExpressionMutation(mutationRate, createRandomExpression ?? (() => "a"))), elitism: 1); - private sealed class DelegateEvaluator : IPopulationEvaluator - { - private readonly Func evaluate; - - public DelegateEvaluator(Func evaluate) => this.evaluate = evaluate; - - public IReadOnlyList EvaluateAll(IReadOnlyList networks) => networks.Select(evaluate).ToList(); - } - [Fact] public void FirstGenerationReplacesNetworksWithoutAViableFitness() { - var queuedNetworks = new Queue(new[] { SingleRuleNetwork("bad"), SingleRuleNetwork("good"), SingleRuleNetwork("good") }); + var queuedNetworks = new Queue(new[] { LoneOutputNeuron("bad"), LoneOutputNeuron("good"), LoneOutputNeuron("good") }); var algorithm = Create(2, queuedNetworks.Dequeue, network => network.Neurons[0].Rules[0].Expression == "good" ? 0.5f : 0f); algorithm.NextGeneration(); @@ -57,8 +49,8 @@ public void FirstGenerationReplacesNetworksWithoutAViableFitness() [Fact] public void FittestIndividualSurvivesIntoTheNextGeneration() { - Network fittest = SingleRuleNetwork("aa"); - var queuedNetworks = new Queue(new[] { SingleRuleNetwork("a"), fittest, SingleRuleNetwork("a") }); + Network fittest = LoneOutputNeuron("aa"); + var queuedNetworks = new Queue(new[] { LoneOutputNeuron("a"), fittest, LoneOutputNeuron("a") }); var algorithm = Create(3, queuedNetworks.Dequeue, network => network == fittest ? 0.9f : 0.1f); algorithm.NextGeneration(); @@ -71,7 +63,7 @@ public void FittestIndividualSurvivesIntoTheNextGeneration() public void LaterGenerationsRecordOnlyInRangeFitnessAndPickBestFromThem() { var fitnessSequence = new Queue(new[] { 0.5f, 0.5f, 0.4f, 1.5f }); - var algorithm = Create(2, () => SingleRuleNetwork("a"), _ => fitnessSequence.Dequeue()); + var algorithm = Create(2, () => LoneOutputNeuron("a"), _ => fitnessSequence.Dequeue()); algorithm.NextGeneration(); Assert.Empty(algorithm.FitnessHistory); @@ -84,7 +76,7 @@ public void LaterGenerationsRecordOnlyInRangeFitnessAndPickBestFromThem() [Fact] public void MutationReplacesARuleExpression() { - var algorithm = Create(2, () => SingleRuleNetwork("a"), _ => 0.5f, mutationRate: 1, createRandomExpression: () => "aaaa"); + var algorithm = Create(2, () => LoneOutputNeuron("a"), _ => 0.5f, mutationRate: 1, createRandomExpression: () => "aaaa"); algorithm.NextGeneration(); @@ -114,7 +106,7 @@ public void TournamentSelectionBreedsMostlyFromTheFittest() int created = 0; var algorithm = Create( 50, - () => SingleRuleNetwork(created++ == 0 ? "aa" : "a"), + () => LoneOutputNeuron(created++ == 0 ? "aa" : "a"), network => network.Neurons[0].Rules[0].Expression == "aa" ? 0.9f : 0.1f, selection: new TournamentSelection(10)); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/ImmigrantsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/ImmigrantsTests.cs new file mode 100644 index 0000000..1352698 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/ImmigrantsTests.cs @@ -0,0 +1,91 @@ +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; +using static SnpEvolution.Tests.Fixtures.GeneticAlgorithms; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Algorithms +{ + public class ImmigrantsTests + { + [Fact] + public void GenerationalImmigrantsReplaceTheWeakestButNotTheElite() + { + var random = new Random(4); + NetworkFactory factory = Factories.StandardRules(random); + GeneticAlgorithm algorithm = Generational(6, random, factory.NewNetwork, new RecordingEvaluator(network => 1f / network.Neurons.Count), WeightedMutation.Structural(1, factory)); + algorithm.NextGeneration(); + Network elite = algorithm.Population[0].Genes; + Network[] newcomers = { NeverOutputs(), NeverOutputs(), NeverOutputs() }; + + algorithm.Immigrate(newcomers); + + Assert.Equal(6, algorithm.Population.Count); + Assert.Same(elite, algorithm.Population[0].Genes); + Assert.Equal(newcomers, algorithm.Population.Skip(3).Select(individual => individual.Genes)); + } + + [Fact] + public void AsManyImmigrantsAsThePopulationStillLeaveTheEliteInPlace() + { + var random = new Random(4); + NetworkFactory factory = Factories.StandardRules(random); + var evaluator = new RecordingEvaluator(network => 1f / network.Neurons.Count); + var algorithms = new IGeneticAlgorithm[] + { + Generational(6, random, factory.NewNetwork, evaluator, WeightedMutation.Structural(1, factory)), + new SpeciatedAlgorithm(6, random, factory.NewNetwork, evaluator, new NeuronCrossover(), WeightedMutation.Structural(1, factory)), + }; + foreach (IGeneticAlgorithm algorithm in algorithms) + { + algorithm.NextGeneration(); + Network elite = algorithm.Population[0].Genes; + + algorithm.Immigrate(Enumerable.Range(0, 6).Select(_ => NeverOutputs()).ToList()); + + Assert.Same(elite, algorithm.Population[0].Genes); + Assert.Equal(6, algorithm.Population.Count); + } + } + + [Fact] + public void NewcomersAreMadeOnlyFromScoredHosts() + { + var scored = new Individual(TestNetworks.Identity()); + scored.Record(new FitnessResult(-1, Array.Empty())); + var unscored = new Individual(NeverOutputs()); + + List made = Immigrants.FromBest(new[] { unscored, scored }, hosts: 1, wanted: 1, attempts: 1, host => host); + + Assert.Same(scored.Genes, Assert.Single(made)); + } + + [Fact] + public void ArchiveAndStrategyImmigrantsJoinTheNextBatch() + { + var random = new Random(5); + NetworkFactory factory = Factories.StandardRules(random); + var evaluator = new RecordingEvaluator(network => 1f / network.Neurons.Count); + var algorithms = new IGeneticAlgorithm[] + { + new MapElites(6, random, factory.NewNetwork, evaluator, new NeuronCrossover(), WeightedMutation.Structural(1, factory)), + new MuPlusLambdaStrategy(2, 6, random, factory.NewNetwork, evaluator, WeightedMutation.Structural(1, factory)), + }; + foreach (IGeneticAlgorithm algorithm in algorithms) + { + algorithm.NextGeneration(); + Network newcomer = NeverOutputs(); + algorithm.Immigrate(new[] { newcomer }); + evaluator.Seen.Clear(); + + algorithm.NextGeneration(); + + Assert.Contains(newcomer, evaluator.Seen); + Assert.Equal(6, evaluator.Seen.Count); + } + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/IterativeEvolutionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/IterativeEvolutionTests.cs new file mode 100644 index 0000000..63622e0 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/IterativeEvolutionTests.cs @@ -0,0 +1,59 @@ +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.GeneticAlgorithms; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Algorithms +{ + public class IterativeEvolutionTests + { + [Fact] + public void IterativeEvolutionSolvesEachStageInTurn() + { + var random = new Random(1); + var task = new SequenceTask("twos", Enumerable.Repeat(2, 6).ToList()); + var seen = new List(); + var iterative = new IterativeEvolution(task, new[] { 2, 4, 6 }, + stageTask => + { + seen.Add(((SequenceTask)stageTask).Expected.Count); + return new FitnessEvaluator(new SequentialCpuEngine(), stageTask, new SimulationOptions(5, 3, OutputTiming.Interval), 2, random, new EvaluationBudget()); + }, + evaluator => Generational(6, random, PingPong, evaluator, WeightedMutation.Structural(0, Factories.StandardRules(random))), + _ => { }); + + for (int generation = 0; generation < 10 && !iterative.IsComplete; generation++) + { + iterative.NextGeneration(); + } + + Assert.True(iterative.IsComplete); + Assert.Equal(new[] { 2, 4, 6 }, seen); + Assert.Equal(new[] { 2, 4, 6 }, iterative.Stages.Select(stage => stage.Length)); + Assert.All(iterative.Stages, stage => Assert.True(stage.Solved)); + Assert.Equal(4, iterative.Generation); + } + + [Fact] + public void ALuckyScoreDoesNotSolveAStageAndIsReplacedByItsFailedRetest() + { + var lucky = new Individual(AlwaysOutputsOne()); + lucky.Record(new FitnessResult(1f, new[] { 1 })); + var algorithm = new StubAlgorithm(); + algorithm.Individuals.Add(lucky); + var iterative = new IterativeEvolution(new SequenceTask("twos", Enumerable.Repeat(2, 4).ToList()), new[] { 2, 4 }, + stageTask => new FitnessEvaluator(new SequentialCpuEngine(), stageTask, new SimulationOptions(5, 3, OutputTiming.Interval), 2, new Random(0), new EvaluationBudget()), + _ => algorithm, + _ => { }); + + iterative.NextGeneration(); + + Assert.False(iterative.Stages.Single().Solved); + Assert.True(lucky.Fitness < 1f); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/MapElitesTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/MapElitesTests.cs new file mode 100644 index 0000000..0ff8c37 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/MapElitesTests.cs @@ -0,0 +1,58 @@ +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; +using static SnpEvolution.Tests.Fixtures.Runs; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Algorithms +{ + public class MapElitesTests + { + [Fact] + public void RescoringTheArchiveUsesTheNewTask() + { + var random = new Random(6); + NetworkFactory factory = Factories.StandardRules(random); + var evaluator = new RecordingEvaluator(_ => 0.9f); + var elites = new MapElites(8, random, factory.NewNetwork, evaluator, new NeuronCrossover(), WeightedMutation.Structural(1, factory)); + elites.NextGeneration(); + int cells = elites.Population.Count; + + evaluator.Fitness = _ => 0.2f; + elites.Rescore(); + + Assert.Equal(0.2f, elites.Best!.Fitness); + Assert.Equal(cells, elites.Population.Count); + Assert.All(elites.Population, elite => Assert.Equal(0.2f, elite.Fitness)); + } + + [Fact] + public void BehaviourNichesReplaceSizeCellsWhenTheTaskHasThem() + { + var individual = new Individual(Identity()); + individual.Record(new FitnessResult(0.5f, Array.Empty())); + Assert.Equal((false, 2, 2), MapElites.CellOf(individual)); + + individual.Record(new FitnessResult(0.5f, Array.Empty(), Niche: (3, 4))); + Assert.Equal((true, 3, 4), MapElites.CellOf(individual)); + } + + [Fact] + public void MapElitesKeepsOneEliteForEachSize() + { + var random = new Random(0); + NetworkFactory factory = Factories.StandardRules(random); + var elites = new MapElites(10, random, factory.NewNetwork, new RecordingEvaluator(ThreeNeurons), new NeuronCrossover(), WeightedMutation.Structural(1, factory)); + + for (int generation = 0; generation < 10; generation++) + { + elites.NextGeneration(); + } + + Assert.Equal(elites.Population.Count, elites.Population.Select(elite => MapElites.Cell(elite.Genes)).Distinct().Count()); + Assert.True(elites.Population.Count > 3); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/MuPlusLambdaStrategyTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/MuPlusLambdaStrategyTests.cs new file mode 100644 index 0000000..f921e81 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/MuPlusLambdaStrategyTests.cs @@ -0,0 +1,23 @@ +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Genome; +using static SnpEvolution.Tests.Fixtures.Runs; + +namespace SnpEvolution.Tests.Search.Algorithms +{ + public class MuPlusLambdaStrategyTests + { + [Fact] + public void MuPlusLambdaKeepsMuParents() + { + var random = new Random(0); + NetworkFactory factory = Factories.StandardRules(random); + var strategy = new MuPlusLambdaStrategy(3, 9, random, factory.NewNetwork, new RecordingEvaluator(ThreeNeurons), WeightedMutation.Structural(1, factory)); + + strategy.NextGeneration(); + strategy.NextGeneration(); + + Assert.Equal(3, strategy.Population.Count); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/RankingTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/RankingTests.cs new file mode 100644 index 0000000..9fcfc30 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/RankingTests.cs @@ -0,0 +1,24 @@ +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Algorithms +{ + public class RankingTests + { + [Fact] + public void RankingPrefersTheSmallerOfEquallyFitNetworks() + { + var small = new Individual(Identity()); + var large = new Individual(ReferenceNetworks.NaturalNumbers()); + small.Record(new FitnessResult(0.5f, Array.Empty())); + large.Record(new FitnessResult(0.5f, Array.Empty())); + + Assert.Same(small, Ranking.Rank(new[] { large, small })[0]); + + large.Record(new FitnessResult(0.6f, Array.Empty())); + Assert.Same(large, Ranking.Rank(new[] { small, large })[0]); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/SpeciatedAlgorithmTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/SpeciatedAlgorithmTests.cs new file mode 100644 index 0000000..5e8c383 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/SpeciatedAlgorithmTests.cs @@ -0,0 +1,17 @@ +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Algorithms +{ + public class SpeciatedAlgorithmTests + { + [Fact] + public void SpeciesDistanceIsZeroOnlyForTheSameStructure() + { + Assert.Equal(0, SpeciatedAlgorithm.Distance(Identity(), Identity())); + Assert.True(SpeciatedAlgorithm.Distance(Identity(), ReferenceNetworks.NaturalNumbers()) > 2); + Assert.Equal(SpeciatedAlgorithm.Distance(Identity(), AlwaysOutputsOne()), SpeciatedAlgorithm.Distance(AlwaysOutputsOne(), Identity())); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/StagnationRecoveryTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/StagnationRecoveryTests.cs new file mode 100644 index 0000000..f6ab692 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/StagnationRecoveryTests.cs @@ -0,0 +1,42 @@ +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; +using static SnpEvolution.Tests.Fixtures.GeneticAlgorithms; + +namespace SnpEvolution.Tests.Search.Algorithms +{ + public class StagnationRecoveryTests + { + [Fact] + public void StagnationEscalatesThenRestartsAndCalmsDownOnANewTask() + { + var random = new Random(2); + NetworkFactory factory = Factories.StandardRules(random); + var pressure = new MutationPressure(); + var evaluator = new RecordingEvaluator(_ => 0.5f); + var recovery = new StagnationRecovery( + Generational(8, random, factory.NewNetwork, evaluator, WeightedMutation.Structural(0.5f, factory, pressure)), + new StagnationPolicy(Patience: 2, ImmigrantFraction: 0.25, MaxExtraEdits: 2), 8, pressure, + factory.NewNetwork, WeightedMutation.Structural(1, factory), random, _ => { }); + + recovery.NextGeneration(); + for (int generation = 0; generation < 4; generation++) + { + recovery.NextGeneration(); + } + Assert.Equal((2, 0, 2), (recovery.Escalations, recovery.Restarts, pressure.ExtraEdits)); + + recovery.NextGeneration(); + recovery.NextGeneration(); + Assert.Equal((1, 0), (recovery.Restarts, pressure.ExtraEdits)); + Assert.Equal(8, recovery.Population.Count); + + recovery.NextGeneration(); + recovery.NextGeneration(); + Assert.Equal(1, pressure.ExtraEdits); + recovery.Rescore(); + Assert.Equal(0, pressure.ExtraEdits); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/StallDetectorTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/StallDetectorTests.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/StallDetectorTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/StallDetectorTests.cs index b821c24..01a50ca 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/StallDetectorTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/StallDetectorTests.cs @@ -1,7 +1,7 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Specs.Accounting; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Search.Algorithms { public class StallDetectorTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/BenchmarkTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/BenchmarkTests.cs new file mode 100644 index 0000000..b74e93f --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/BenchmarkTests.cs @@ -0,0 +1,74 @@ +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Parts; +using static SnpEvolution.Tests.Fixtures.BenchmarkFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Benchmarking +{ + public class BenchmarkTests + { + [Fact] + public void BenchmarkSummarisesRunsPerAlgorithmAndTask() + { + var best = new Individual(Identity()); + RunOutcome[] outcomes = + { + new RunOutcome("A", "T", 1, true, 100, 1, best), + new RunOutcome("A", "T", 2, true, 300, 1, best), + new RunOutcome("A", "T", 3, false, 1000, 0.5f, null), + }; + + BenchmarkRow row = Assert.Single(Benchmark.Summarise(outcomes)); + + Assert.Equal((3, 2, 200.0), (row.Runs, row.Solved, row.MedianEvaluationsToSolve!.Value)); + Assert.Equal(2.5 / 3, row.MeanBestFitness, precision: 5); + Assert.Equal(Identity().Size, row.MeanSolvedSize); + Assert.Contains("2/3", Benchmark.FormatTable(new[] { row })); + Assert.StartsWith("task,algorithm", Benchmark.FormatCsv(new[] { row })); + } + + [Fact] + [Slow] + public void SelectorHalvesTheCandidatesUntilOneIsLeft() + { + List candidates = SearchCatalog.Evolution.Take(3).ToList(); + + SelectionResult result = AlgorithmSelector.Select(candidates, TaskSuite.Generators[0], OneSeed, initialBudget: 60); + + Assert.Contains(result.Winner, candidates); + Assert.Equal(new[] { 3, 2 }, result.Rounds.Select(round => round.Standings.Count)); + Assert.Equal(new[] { 60L, 120L }, result.Rounds.Select(round => round.Budget)); + Assert.Equal(result.Rounds[0].Standings.Take(2).Select(row => row.Algorithm), result.Rounds[0].Advancing); + Assert.NotNull(result.BestFound); + } + + [Fact] + public void TheBenchmarkTableReportsWhichPartsTheBestNetworksHold() + { + var outcomes = new[] + { + new RunOutcome("composition", "multiply", 1, false, 100, 0.9f, null, new[] { new PartCount("add loop", 1, 0), new PartCount("register", 0, 2) }), + new RunOutcome("composition", "multiply", 2, false, 300, 0.8f, null, new[] { new PartCount("add", 2, 0) }), + }; + + BenchmarkRow row = Benchmark.Summarise(outcomes).Single(); + string table = Benchmark.FormatTable(new[] { row }); + + Assert.Equal("add 1/2, add loop 1/2", row.ReuseText); + Assert.Equal(2, row.Reuse!.Single(use => use.Contract == "add").MeanCopies); + Assert.Contains("Parts in best (runs)", table); + Assert.Contains("add loop 1/2", Benchmark.FormatCsv(new[] { row })); + } + + [Fact] + public void AFlatRunReportsNoPartColumn() + { + BenchmarkRow row = Benchmark.Summarise(new[] { new RunOutcome("flat", "n + 1", 1, false, 100, 0.5f, null) }).Single(); + + Assert.Null(row.Reuse); + Assert.DoesNotContain("Parts in best", Benchmark.FormatTable(new[] { row })); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/TaskSuiteTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/TaskSuiteTests.cs new file mode 100644 index 0000000..912f3b8 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/TaskSuiteTests.cs @@ -0,0 +1,21 @@ +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using static SnpEvolution.Tests.Fixtures.Runs; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Benchmarking +{ + public class TaskSuiteTests + { + [Fact] + public void SuiteTasksAreAllUsableWithTheirOwnSettings() + { + foreach (BenchmarkTask task in TaskSuite.All) + { + Assert.NotEmpty(task.Task.Cases); + FitnessResult result = Runs.Evaluate(task.Task, Identity(), options: TaskOptions, solvedRetestCount: 3); + Assert.InRange(result.Fitness, 0, 1); + } + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/CompositionSearchTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/CompositionSearchTests.cs new file mode 100644 index 0000000..8958197 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/CompositionSearchTests.cs @@ -0,0 +1,148 @@ +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Storage; +using static SnpEvolution.Tests.Fixtures.CompositionFixtures; + +namespace SnpEvolution.Tests.Search +{ + public class CompositionSearchTests + { + private sealed class FixedEdit : IMutation + { + private readonly Network result; + + public FixedEdit(Network result) => this.result = result; + + public Network Mutate(Network network, Random random) => result; + } + + [Fact] + [Slow] + public void CompositionsStillRoundTripAfterAnyEditTheSearchMakes() + { + ModuleLibrary library = Library(); + for (int seed = 0; seed < 50; seed++) + { + var random = new Random(seed); + var space = new CompositionSpace(library, GlueFactory(seed % 2, random), new CompositionMix(), random); + WeightedMutation mutation = space.Mutation(1); + Network network = space.NewNetwork(); + for (int step = 0; step < 20; step++) + { + network = mutation.Mutate(network, random); + + Composition composition = Composition.Recover(network, library)!; + Assert.Equal(composition, Composition.Recover(composition.Flatten(library), library)); + Assert.Equal(NetworkFiles.ToJson(network), NetworkFiles.ToJson(composition.Flatten(library))); + Assert.True(composition.ThroughPorts(library)); + Assert.InRange(composition.Glue.Count, 1, MaxGlue); + } + } + } + + [Fact] + public void TheSearchAdmitsNoMoreGlueThanItsCap() + { + ModuleLibrary library = Library(); + var random = new Random(6); + Composition composition = RandomComposition.Of(library, GlueFactory(0, random), 1, random); + var space = new CompositionSpace(library, GlueFactory(0, random), new CompositionMix(MaxGlue: composition.Glue.Count), random); + Composition overGlued = composition with { Glue = composition.Glue.Append(composition.Glue[0]).ToList() }; + + Assert.NotNull(space.Admit(composition.Flatten(library), dropStrayLinks: false)); + Assert.Null(space.Admit(overGlued.Flatten(library), dropStrayLinks: false)); + } + + // Glue may stop reading a part's port, but a glue edit that starts reading one would change how the part is used. + [Fact] + public void AGlueEditMayNotAddASynapseFromAPartToGlue() + { + ModuleLibrary library = Library(); + var random = new Random(8); + var space = new CompositionSpace(library, GlueFactory(0, random), new CompositionMix(), random); + Composition composition = RandomComposition.Of(library, GlueFactory(0, random), 1, random); + PartInstance part = composition.Parts[0]; + PartPort outPort = Composition.PartOf(part, library).Part.Ports().First(port => port.Port.Direction == PortDirection.Out); + var sender = new Endpoint(part.Instance, outPort.Position); + int unread = Enumerable.Range(1, composition.Glue.Count).First(glue => !composition.Links.Contains(new Link(sender, Endpoint.GlueAt(glue)))); + Network network = composition.Flatten(library); + Network reading = (composition with { Links = composition.Links.Append(new Link(sender, Endpoint.GlueAt(unread))).ToList() }).Flatten(library); + + Assert.Same(network, new CompositionEdit(space, new FixedEdit(reading), glueOnly: true).Mutate(network, random)); + } + + [Fact] + public void RemovingAPartDropsItsWiresAndKeepsTheRest() + { + ModuleLibrary library = Library(); + for (int seed = 0; seed < 20; seed++) + { + var random = new Random(seed); + Network network = RandomComposition.Of(library, GlueFactory(0, random), 3, random).Flatten(library); + Composition before = Composition.Recover(network, library)!; + + Composition after = Composition.Recover(new RemovePart(library).Mutate(network, random), library)!; + + Assert.True(after.Parts.Count >= before.Parts.Count - 1); + Assert.All(after.Parts, part => Assert.Contains(part, before.Parts)); + Assert.All(after.Wires, wire => Assert.Contains(wire, before.Wires)); + Assert.Equal(before.Glue.Count, after.Glue.Count); + } + } + + [Fact] + [Slow] + public void ABenchmarkRunOfCompositionSearchBuildsFromTheGivenParts() + { + ModuleLibrary library = Library(); + BenchmarkSettings settings = BenchmarkSettings.Default with { Seeds = 1, Parts = library.Parts.Select(module => module.Part!).ToList() }; + EvolutionSearch algorithm = SearchCatalog.CompositionMapElites; + + RunOutcome outcome = Benchmark.RunOnce(algorithm, TaskSuite.Functions.First(task => task.Name == "Compute n + 1"), seed: 1, budget: 400, settings); + + Assert.Contains(outcome.Best!.Genes.Neurons, neuron => neuron.Module != null); + } + + // Every network a composition run keeps is exactly glue and library parts: no part neuron is ever changed. + [Theory] + [Slow] + [InlineData(true)] + [InlineData(false)] + public void ARunInCompositionModeNeverChangesAPartNeuron(bool mapElites) + { + ModuleLibrary library = Library(); + var random = new Random(5); + NetworkFactory factory = GlueFactory(1, random); + var task = FunctionTask.Of("n + 2", n => n + 2, new[] { 1, 2, 3 }); + var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(30, 2, OutputTiming.Interval), 1, random, new EvaluationBudget()); + IGeneticAlgorithm run = (mapElites ? SearchCatalog.CompositionMapElites : SearchCatalog.CompositionTournament) + .Create(new EvolutionContext(16, 0.5f, random, factory.NewNetwork, evaluator, factory, _ => { }, Parts: library)); + + bool sawParts = false; + for (int generation = 0; generation < 30; generation++) + { + run.NextGeneration(); + sawParts |= run.Population.Any(individual => individual.Genes.Neurons.Any(neuron => neuron.Module != null)); + Assert.All(run.Population, individual => + { + Composition? composition = Composition.Recover(individual.Genes, library); + Assert.NotNull(composition); + Assert.True(composition!.ThroughPorts(library)); + Assert.InRange(composition.Glue.Count, 1, MaxGlue); + }); + } + Assert.True(sawParts); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/EvolutionContextTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/EvolutionContextTests.cs new file mode 100644 index 0000000..8ef3d53 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/EvolutionContextTests.cs @@ -0,0 +1,70 @@ +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Genome; + +namespace SnpEvolution.Tests.Search +{ + public class EvolutionContextTests + { + private static readonly GenomeSpace ProfileSpace = new GenomeSpace(InputCount: 2, RuleForm: RuleForm.Mixed, MaxNeurons: 8, MaxDelay: 3, DuplicateNeurons: true, HardwareProfile: true); + + // A fitness and checks that vary with size, so selection and lexicase have something to choose by. + private static RecordingEvaluator Evaluator() => new RecordingEvaluator(network => 1f / (1 + network.Size % 7), network => new[] { network.Size % 2f }); + + private static NetworkFactory Factory(int seed) + { + var random = new Random(seed); + return Factories.Networks(ProfileSpace, random); + } + + [Fact] + public void NetworksMadeUnderTheProfileFitItWithoutBeingConformed() + { + NetworkFactory factory = Factory(1); + + for (int network = 0; network < 300; network++) + { + Network made = factory.NewNetwork(); + Assert.Empty(HardwareProfile.Problems(made)); + } + } + + [Fact] + public void NoNetworkMadeOrMutatedByAnyAlgorithmUnderTheProfileBreaksIt() + { + foreach (EvolutionSearch choice in SearchCatalog.Evolution.Where(search => search is not CompositionSearch)) + { + NetworkFactory factory = Factory(choice.Name.Length); + var evaluator = Evaluator(); + IGeneticAlgorithm algorithm = choice.Create(new EvolutionContext(20, 1f, factory.Random, factory.NewNetwork, evaluator, factory, _ => { }, Lexicase: true)); + + for (int generation = 0; generation < 15; generation++) + { + algorithm.NextGeneration(); + } + + Assert.True(evaluator.Seen.Count > 100, choice.Name); + Network? broken = evaluator.Seen.FirstOrDefault(network => !HardwareProfile.Fits(network)); + Assert.True(broken == null, $"{choice.Name} made a network outside the profile: {string.Join(" ", broken == null ? new string[0] : HardwareProfile.Problems(broken))}"); + } + } + + [Fact] + public void EveryStructuralEditIsPutBackWithinTheProfile() + { + NetworkFactory factory = Factory(7); + var context = new EvolutionContext(10, 1f, factory.Random, factory.NewNetwork, Evaluator(), factory, _ => { }); + foreach (var edit in context.StructuralMutation(1).Edits) + { + var conformed = context.Conformed(edit.Edit); + Network network = factory.NewNetwork(); + for (int step = 0; step < 200; step++) + { + network = conformed.Mutate(network, factory.Random); + Assert.True(HardwareProfile.Fits(network), $"{edit.Name}: {string.Join(" ", HardwareProfile.Problems(network))}"); + } + } + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/EvolutionSearchTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/EvolutionSearchTests.cs new file mode 100644 index 0000000..cf3cdad --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/EvolutionSearchTests.cs @@ -0,0 +1,72 @@ +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.BenchmarkFixtures; +using static SnpEvolution.Tests.Fixtures.Runs; + +namespace SnpEvolution.Tests.Search +{ + public class EvolutionSearchTests + { + [Fact] + public void AnEvolutionSearchStartsFromItsSeed() + { + var random = new Random(4); + NetworkFactory factory = Factories.StandardRules(random); + var scoring = new NetworkScoring(() => new SequentialCpuEngine(), new SimulationOptions(20, 1, OutputTiming.Interval), 1); + var setup = new NetworkSetup(4, 0.5f, factory, () => throw new InvalidOperationException("The seed should be used."), scoring); + var seed = new Individual(TestNetworks.Identity()); + + SearchOutcome outcome = SearchCatalog.StructuralDefault.Run( + new SearchRequest(FunctionTask.Of("n", n => n, new[] { 1, 2 }), new EvaluationBudget(), random, _ => { }) { Seeds = new[] { seed }, MaxGenerations = 1, Networks = setup }); + + Assert.Same(seed.Genes, outcome.Best!.Genes); + } + + public static TheoryData StructuralAlgorithms => new TheoryData( + SearchCatalog.Evolution.Where(search => !search.EvolvesRulesOnly).Select(choice => choice.Name)); + + private static readonly BenchmarkTask Identity = TaskSuite.Functions.First(task => task.Name == "Compute n"); + + [Theory] + [Slow] + [MemberData(nameof(StructuralAlgorithms))] + public void StructuralAlgorithmsSolveTheIdentityFunction(string name) + { + EvolutionSearch algorithm = SearchCatalog.Evolution.Single(search => search.Name == name); + + RunOutcome outcome = Benchmark.RunOnce(algorithm, Identity, seed: 1, budget: 4000, OneSeed); + + Assert.True(outcome.Solved, $"{name} reached {outcome.BestFitness}"); + Assert.True(outcome.Best!.Exact); + } + + [Theory] + [MemberData(nameof(StructuralAlgorithms))] + public void BestNeverGetsWorseAndPopulationStaysFilled(string name) + { + var random = new Random(3); + NetworkFactory factory = Factories.StandardRules(random); + var evaluator = new RecordingEvaluator(ThreeNeurons); + IGeneticAlgorithm algorithm = SearchCatalog.Evolution.Single(search => search.Name == name) + .Create(new EvolutionContext(12, 0.5f, random, factory.NewNetwork, evaluator, factory, _ => { })); + + float best = float.MinValue; + for (int generation = 0; generation < 15; generation++) + { + algorithm.NextGeneration(); + Assert.True(algorithm.Best!.Fitness >= best); + best = algorithm.Best.Fitness; + Assert.NotEmpty(algorithm.Population); + } + + Assert.Equal(1f, best); + Assert.Equal(16, algorithm.Generation); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Fitness/CheckDiagnosisTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Fitness/CheckDiagnosisTests.cs new file mode 100644 index 0000000..b98e8ab --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Fitness/CheckDiagnosisTests.cs @@ -0,0 +1,20 @@ +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Fitness +{ + public class CheckDiagnosisTests + { + [Fact] + public void DiagnosisFindsTheFirstCheckNoNetworkDoes() + { + CheckDiagnosis diagnosis = CheckDiagnosis.Of(new[] { Scored(PingPong(), 0.5f, 1, 1, 0, 0), Scored(Identity(), 0.4f, 1, 0, 0.5f, 1) }); + + Assert.Equal(new[] { 2 }, diagnosis.Unsolved); + Assert.Equal(2, diagnosis.Frontier); + Assert.StartsWith("No network yet does gap 3 (5)", diagnosis.Describe(new SequenceTask("fib", new[] { 1, 2, 5, 8 }))); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/FitnessEvaluatorTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Fitness/FitnessEvaluatorTests.cs similarity index 78% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/FitnessEvaluatorTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Fitness/FitnessEvaluatorTests.cs index 660c277..7d9e2ac 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/FitnessEvaluatorTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Fitness/FitnessEvaluatorTests.cs @@ -1,11 +1,13 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.Runs; +using static SnpEvolution.Tests.Fixtures.TestNetworks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Search.Fitness { public class FitnessEvaluatorTests { @@ -120,17 +122,6 @@ public void EvaluateAllScoresEachNetworkInOrder() Assert.Equal(new[] { 0f, 1f }, results.Select(result => result.Fitness)); } - [Theory] - [InlineData(0.985f, true)] - [InlineData(1f, true)] - [InlineData(0.984f, false)] - [InlineData(1.01f, false)] - [InlineData(float.NaN, false)] - public void SolvingFitnessIsBetweenTheThresholdAndOne(float fitness, bool solving) - { - Assert.Equal(solving, Solved.Solves(fitness)); - } - private sealed class ScriptedRandom : Random { private readonly Queue choices; @@ -139,5 +130,29 @@ private sealed class ScriptedRandom : Random public override int Next(int maxValue) => maxValue == 1 ? 0 : choices.Dequeue(); } + + [Fact] + public void ExactResultIsReliablySolvedWithoutRetesting() + { + var budget = new EvaluationBudget(); + var evaluator = new FitnessEvaluator(new ExhaustiveCpuEngine(), FunctionTask.Of("n", n => n, new[] { 1, 2 }), TaskOptions, solvedRetestCount: 5, new Random(0), budget); + + Assert.True(evaluator.ConfirmSolved(Identity()).Solved); + Assert.Equal(1, budget.Networks); + } + + [Fact] + public void RetestsOfASolvedNetworkCountAsVerification() + { + var counter = new EvaluationBudget(); + var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), FunctionTask.Of("n", n => n, new[] { 1, 2 }), + new SimulationOptions(40, 5, OutputTiming.Interval), 3, new Random(1), counter, EvaluationSource.SideRun); + + evaluator.Evaluate(TestNetworks.Identity()); + Assert.True(evaluator.ConfirmSolved(TestNetworks.Identity()).Solved); + + Assert.Equal(1, counter[EvaluationSource.SideRun]); + Assert.Equal(3, counter[EvaluationSource.Verification]); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Genome/NetworkFactoryTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Genome/NetworkFactoryTests.cs new file mode 100644 index 0000000..a796a98 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Genome/NetworkFactoryTests.cs @@ -0,0 +1,34 @@ +using SnpEvolution.Search; +using SnpEvolution.Search.Genome; +using static SnpEvolution.Tests.Fixtures.WellFormedNetworks; + +namespace SnpEvolution.Tests.Search.Genome +{ + public class NetworkFactoryTests + { + [Fact] + public void IsOffUnlessAskedFor() + { + Assert.False(new GenomeSpace().HardwareProfile); + Assert.False(new PartSearchSettings(1, 1, 1, SearchCatalog.Evolution[0], () => null!).HardwareProfile); + } + + [Fact] + public void RandomNetworksAreWellFormed() + { + for (int seed = 0; seed < 100; seed++) + { + NetworkFactory factory = SmallFactory(seed, inputCount: seed % 3); + AssertWellFormed(factory.NewNetwork(), factory.Space); + } + } + + [Fact] + public void StandardSpaceOnlyMakesStandardRules() + { + NetworkFactory factory = SmallFactory(1, form: RuleForm.Standard); + + Assert.All(Enumerable.Range(0, 50).Select(_ => factory.NewRule()), rule => Assert.True(rule.IsStandard)); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/NetworkGenerationTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Genome/NetworkGenerationTests.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/NetworkGenerationTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Genome/NetworkGenerationTests.cs index c775b62..190f4ef 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/NetworkGenerationTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Genome/NetworkGenerationTests.cs @@ -1,8 +1,8 @@ using System.Text.RegularExpressions; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Search.Genome { public class NetworkGenerationTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/HandBuiltMachinesTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/HandBuiltMachinesTests.cs similarity index 55% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/HandBuiltMachinesTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Modules/HandBuiltMachinesTests.cs index f15af0e..f83a394 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/HandBuiltMachinesTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/HandBuiltMachinesTests.cs @@ -1,11 +1,14 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Modules; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; using Xunit.Abstractions; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Search.Modules { public class HandBuiltMachinesTests { @@ -49,5 +52,32 @@ public void TheHandBuiltLibraryHoldsThePromotedAddLoopBuiltFromParts() Assert.Equal(BoundedCheck.Admission(loop.Contract).MaxBound, loop.Proven?.UpTo); output.WriteLine(string.Join(Environment.NewLine, log)); } + + private static BenchmarkSettings Settings(ModuleLibrary library) => BenchmarkSettings.Default with + { + Seeds = 1, + PopulationSize = 40, + Repetitions = 2, + Lexicase = true, + CreateEngine = () => new SequentialCpuEngine(), + Parts = library.Parts.Select(module => module.Part!).ToList(), + }; + + private static BenchmarkTask Multiply => TaskSuite.Contracts.Single(task => task.Name == "Contract multiply"); + + private static EvolutionSearch MapElites => SearchCatalog.CompositionMapElites; + + [Fact] + [Slow] + public void CompositionSearchSolvesMultiplicationByReusingThePromotedAddLoop() + { + RunOutcome outcome = Benchmark.RunOnce(MapElites, Multiply, seed: 3, budget: 3_000, Settings(HandBuiltMachines.Library())); + + output.WriteLine($"Solved in {outcome.Evaluations} evaluations; parts in best: {string.Join(", ", outcome.Reuse!.Select(count => $"{count.Contract} {count.Direct} ({count.Nested})"))}"); + Assert.True(outcome.Solved); + Assert.True(outcome.Promoted); + Assert.Equal(1, outcome.Reuse!.Single(count => count.Contract == "add loop").Direct); + Assert.Equal(2, outcome.Reuse!.Single(count => count.Contract == "register").Nested); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModularEvolutionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModularEvolutionTests.cs similarity index 78% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ModularEvolutionTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModularEvolutionTests.cs index c2e7a1f..e8d3dbb 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModularEvolutionTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModularEvolutionTests.cs @@ -1,40 +1,14 @@ -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution +using SnpEvolution.Model; +using SnpEvolution.Search.Modules; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Modules { public class ModularEvolutionTests { - // A fixed population that records what it is given, so the modular loop can be watched without evolving. - internal sealed class StubAlgorithm : IGeneticAlgorithm - { - public List Individuals { get; } = new List(); - - public List Immigrants { get; } = new List(); - - public int Rescores { get; private set; } - - public IReadOnlyList Population => Individuals; - - public int Generation { get; private set; } = 1; - - public Individual? Best => Individuals.Count == 0 ? null : Ranking.Rank(Individuals)[0]; - - public IReadOnlyList> FitnessHistory => Array.Empty>(); - - public void NextGeneration() => Generation++; - - public void Immigrate(IReadOnlyList newcomers) => Immigrants.AddRange(newcomers); - - public void Rescore() => Rescores++; - } - [Fact] public void AStallBuildsAModuleOnTheSideAndOffersItToTheBestNetworks() { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModuleMutationsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModuleMutationsTests.cs new file mode 100644 index 0000000..bba9558 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModuleMutationsTests.cs @@ -0,0 +1,67 @@ +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.PartWiringFixtures; + +namespace SnpEvolution.Tests.Search.Modules +{ + public class ModuleMutationsTests + { + // The task's input feeds the part's in-port and its out-port feeds the task's, rather than any glue neuron at random. + [Fact] + public void AnInsertedPartIsWiredToTheTasksOwnPorts() + { + var library = new ModuleLibrary(); + library.AddPart(Verified(ReferenceParts.Register()), "a test"); + var task = new ContractTask(FirstParts.Named("register")); + var glue = new Network(Enumerable.Range(1, 4).Select(position => new Neuron(new[] { Rule.Standard("a", 1) }, 0, Array.Empty(), false, isInput: position <= 2)).ToList()); + var insert = new InsertModule(library, new GenomeSpace(InputCount: 2, RuleForm: RuleForm.Standard, MaxNeurons: MaxNeurons), task.Boundary); + + for (int seed = 0; seed < 10; seed++) + { + Network network = insert.Mutate(glue, new Random(seed)); + PartCopy copy = PartWiring.Copies(network, library).Single(); + + Assert.Contains(copy["n"], network.Neurons[1].Connections); + Assert.Contains(task.Binding["out"], network.Neurons[copy["out"] - 1].Connections); + } + } + + [Fact] + public void EditsWithModulesKeepNetworksWellFormedAndModulesWhole() + { + for (int seed = 0; seed < 20; seed++) + { + var random = new Random(seed); + var factory = Factories.Networks(new GenomeSpace(InputCount: seed % 2, RuleForm: RuleForm.Standard, MaxNeurons: 12), random); + var library = new ModuleLibrary(); + library.Add(ModuleCuts.Whole(Chain()), "a test"); + library.Add(ModuleCuts.Cut(Chain(), new[] { 1 }), "a test"); + Part increment = seed % 2 == 0 ? ReferenceParts.Increment() : PartFixtures.PaddedIncrement(); + library.AddPart(PartFixtures.Measured(increment), "a test"); + WeightedMutation mutation = WeightedMutation.Structural(1, factory, modules: new ModuleSupport(library)); + Network network = factory.NewNetwork(); + for (int step = 0; step < 150; step++) + { + WeightedEdit edit = mutation.Edits[random.Next(mutation.Edits.Count)]; + Network next = edit.Edit.Mutate(network, random); + Assert.True(edit.Edit is DissolveModule or SwapPart || ModuleEdits.KeepsModules(network, next), edit.Name); + network = next; + Assert.Single(network.Neurons, neuron => neuron.IsOutput); + Assert.InRange(network.Neurons.Count, 1, factory.Space.MaxNeurons); + for (int position = 1; position <= network.Neurons.Count; position++) + { + Assert.DoesNotContain(position, network.Neurons[position - 1].Connections); + Assert.All(network.Neurons[position - 1].Connections, target => Assert.InRange(target, 1, network.Neurons.Count)); + } + } + } + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleLibraryTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModuleTrackerTests.cs similarity index 55% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleLibraryTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModuleTrackerTests.cs index 14151d2..6c57f30 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleLibraryTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/ModuleTrackerTests.cs @@ -1,40 +1,14 @@ -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; -using static SnpEvolution.Tests.TestNetworks; - -namespace SnpEvolution.Tests.Evolution +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Modules; +using SnpEvolution.Specs.Parts; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Modules { - public class ModuleLibraryTests + public class ModuleTrackerTests { - private sealed class DelegateEvaluator : IPopulationEvaluator - { - public DelegateEvaluator(Func result) => Result = result; - - public Func Result { get; } - - public IReadOnlyList EvaluateAll(IReadOnlyList networks) => networks.Select(Result).ToList(); - } - - [Fact] - public void TheLibraryKeepsEachPartOnceAndDropsTheLeastHelpfulWhenFull() - { - var library = new ModuleLibrary(capacity: 2); - Module first = ModuleOf(library, Chain()); - Module second = ModuleOf(library, PingPong()); - - Assert.Same(first, ModuleOf(library, Chain())); - library.Credit(first.Id, improved: false); - library.Credit(second.Id, improved: true); - ModuleOf(library, AlwaysOutputsOne()); - - Assert.Equal(2, library.Modules.Count); - Assert.Null(library.Find(first.Id)); - Assert.Equal((1, 1), (second.Uses, second.Wins)); - } - [Fact] public void TheTrackerCreditsNewModulesAndKeepsChangesThatGetANewCheckRight() { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/PartMutationsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/PartMutationsTests.cs new file mode 100644 index 0000000..ca06d24 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/PartMutationsTests.cs @@ -0,0 +1,106 @@ +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.PartWiringFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Modules +{ + public class PartMutationsTests + { + private static NetworkFactory Factory(int maxNeurons, Random random) => + Factories.Networks(new GenomeSpace(InputCount: 2, RuleForm: RuleForm.Standard, MaxNeurons: maxNeurons), random); + + [Fact] + public void RewiringOnlyEverJoinsFittingPortsAndNeverFeedsAnInput() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); + Network chained = Chained(library, increment, 1); + Network network = ModuleEdits.Insert(chained, increment, 3, MaxNeurons, library, new Random(1)); + var rewire = new RewirePort(library); + var seen = new HashSet(); + + for (int seed = 0; seed < 40; seed++) + { + Network rewired = rewire.Mutate(network, new Random(seed)); + + IReadOnlyList copies = PartWiring.Copies(rewired, library); + Assert.Equal(3, copies.Count); + Dictionary ports = copies.SelectMany(copy => copy.Ports).ToDictionary(port => port.Position); + foreach (CopyPort from in ports.Values) + { + foreach (int target in rewired.Neurons[from.Position - 1].Connections.Where(ports.ContainsKey).Where(target => ports[target].Copy != from.Copy)) + { + Assert.True(PartWiring.Fits(from.Port, ports[target].Port), $"{from.Port.Name} > {ports[target].Port.Name}"); + } + } + Assert.All(rewired.Neurons, neuron => Assert.DoesNotContain(neuron.Connections, target => rewired.Neurons[target - 1].IsInput)); + seen.Add(string.Join(" ", PartWiring.Wires(rewired, library).Select(WireText))); + } + Assert.True(seen.Count > 1); + } + + [Fact] + public void AGlueNeuronSitsOnAWireAndThePairStillAddsTwo() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); + Network network = Chained(library, increment, 1); + var random = new Random(1); + + Network glued = new AddGlueNeuron(library, Factory(MaxNeurons, random)).Mutate(network, random); + + Assert.Equal(network.Neurons.Count + 1, glued.Neurons.Count); + Assert.Single(PartWiring.Wires(glued, library)); + Assert.Null(glued.Neurons[^1].Module); + PartMeasurement measurement = MeasurePlusTwo(glued, library); + Assert.True(measurement.Verdict is Verdict.Passed, measurement.Description); + Assert.Same(network, new AddGlueNeuron(library, Factory(network.Neurons.Count, random)).Mutate(network, random)); + } + + [Fact] + public void APartIsSwappedForACheaperOneWithTheSameContractAndKeepsItsWires() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(PartFixtures.PaddedIncrement()), "a test"); + Network padded = Chained(library, increment, 1); + library.AddPart(Verified(ReferenceParts.Increment()), "a cheaper test"); + var swap = new SwapPart(library); + + Network once = swap.Mutate(padded, new Random(1)); + Network twice = swap.Mutate(once, new Random(1)); + + Assert.Equal(new[] { 12, 11, 10 }, new[] { padded, once, twice }.Select(network => network.Neurons.Count)); + Assert.Same(twice, swap.Mutate(twice, new Random(1))); + Assert.Equal(new[] { "1.out>2.n", "1.done>2.start" }, PartWiring.Wires(twice, library).Select(WireText)); + Assert.Equal(new[] { true, true }, twice.Neurons.Take(2).Select(neuron => neuron.IsInput)); + PartMeasurement measurement = MeasurePlusTwo(twice, library); + Assert.True(measurement.Verdict is Verdict.Passed, measurement.Description); + // A role on a neuron with no port of the same name would be lost, so that copy is not swapped. + Network outputOnPadding = padded.WithNeuron(11, padded.Neurons[11].WithRoles(true, false)); + Assert.All(Enumerable.Range(0, 10), seed => Assert.Single(swap.Mutate(outputOnPadding, new Random(seed)).Neurons, neuron => neuron.IsOutput)); + } + + [Fact] + public void PartEditsAreOnlyOfferedWhenTheLibraryHoldsParts() + { + var harvested = new ModuleLibrary(); + harvested.Add(ModuleCuts.Whole(PingPong()), "a test"); + var withParts = new ModuleLibrary(); + withParts.AddPart(Verified(ReferenceParts.Increment()), "a test"); + NetworkFactory factory = Factory(MaxNeurons, new Random(1)); + + IEnumerable PartEdits(ModuleLibrary library) => + WeightedMutation.Structural(1, factory, modules: new ModuleSupport(library)).Edits.Where(edit => edit.Edit is RewirePort or AddGlueNeuron or SwapPart); + + Assert.Empty(PartEdits(harvested)); + Assert.Equal(3, PartEdits(withParts).Count()); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/PromotionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/PromotionTests.cs similarity index 87% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/PromotionTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Modules/PromotionTests.cs index 9afbced..1ecfa38 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/PromotionTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Modules/PromotionTests.cs @@ -1,27 +1,22 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Search.Modules; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Search.Modules { public sealed class PromotionTests : IDisposable { - private readonly string folder = Path.Combine(Path.GetTempPath(), "promotion-" + Guid.NewGuid().ToString("N")); + private readonly TempFolder temp = new TempFolder("promotion"); private readonly List log = new List(); - public void Dispose() - { - if (Directory.Exists(folder)) - { - Directory.Delete(folder, recursive: true); - } - } + private string folder => temp.Path; + + public void Dispose() => temp.Dispose(); private static readonly PartOrigin Origin = new PartOrigin(1, "a test", 0); @@ -47,8 +42,6 @@ private static (ModuleLibrary Library, Module Increment) Increments() return (library, library.AddPart(Verified(ReferenceParts.Increment()), "a test")); } - private static string Json(Network network) => NetworkFiles.ToJson(network); - [Fact] public void TheTwoIncrementChainPromotedAsAddTwoLoadsFromDiskAndFlattensToTheSameNetwork() { @@ -62,7 +55,7 @@ public void TheTwoIncrementChainPromotedAsAddTwoLoadsFromDiskAndFlattensToTheSam ModuleLibrary loaded = PartLibraryFiles.Load(folder); LibraryPart addTwo = loaded.PartFor("add 2")!.Part!; - Assert.Equal(Json(chain.Flatten(library)), Json(addTwo.Part.Network)); + Assert.Equal(NetworkFiles.ToJson(chain.Flatten(library)), NetworkFiles.ToJson(addTwo.Part.Network)); Assert.Equal(promoted.Part!.Part.Binding.Positions, addTwo.Part.Binding.Positions); Assert.Equal(promoted.Part.Cost, addTwo.Cost); Assert.Contains(log, line => line.StartsWith("Promoted the composition for add 2")); @@ -128,8 +121,8 @@ public void PartsBuiltFromPromotedPartsLoadAfterTheirChildren() Assert.Equal(new[] { "add 2", "add 4", "increment" }, loaded.Parts.Select(module => module.Part!.Contract.Name).Order()); Assert.Equal( - Json(library.PartFor("add 4")!.Part!.Part.Network), - Json(loaded.PartFor("add 4")!.Part!.Part.Network)); + NetworkFiles.ToJson(library.PartFor("add 4")!.Part!.Part.Network), + NetworkFiles.ToJson(loaded.PartFor("add 4")!.Part!.Part.Network)); } [Fact] diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Operators/LexicaseSelectionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Operators/LexicaseSelectionTests.cs new file mode 100644 index 0000000..25ec4f5 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Operators/LexicaseSelectionTests.cs @@ -0,0 +1,31 @@ +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Operators; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search.Operators +{ + public class LexicaseSelectionTests + { + [Fact] + public void LexicasePicksSpecialistsButNeverANetworkThatIsBestAtNothing() + { + var random = new Random(1); + List ranked = Ranking.Rank(new[] + { + Scored(PingPong(), 0.6f, 0.6f, 0.6f), + Scored(Identity(), 0.5f, 1f, 0f), + Scored(NeverOutputs(), 0.5f, 0f, 1f), + Scored(Chain(), 0.4f, 0.5f, 0.5f), + }); + Func pick = new LexicaseSelection().Prepare(ranked, random); + + List picks = Enumerable.Range(0, 200).Select(_ => pick()).ToList(); + + Assert.Contains(picks, individual => individual.Fitness == 0.5f && individual.Checks[1] == 1f); + Assert.Contains(picks, individual => individual.Fitness == 0.5f && individual.Checks[0] == 1f); + Assert.DoesNotContain(picks, individual => individual.Fitness == 0.4f); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Operators/StructuralMutationsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Operators/StructuralMutationsTests.cs new file mode 100644 index 0000000..7d4a1f9 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Operators/StructuralMutationsTests.cs @@ -0,0 +1,90 @@ +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; +using static SnpEvolution.Tests.Fixtures.TestNetworks; +using static SnpEvolution.Tests.Fixtures.WellFormedNetworks; + +namespace SnpEvolution.Tests.Search.Operators +{ + public class StructuralMutationsTests + { + [Fact] + public void EveryEditKeepsNetworksWellFormed() + { + for (int seed = 0; seed < 30; seed++) + { + NetworkFactory factory = SmallFactory(seed, inputCount: seed % 3); + WeightedMutation mutation = WeightedMutation.Structural(1, factory); + var crossover = new NeuronCrossover(); + var random = new Random(seed); + Network network = factory.NewNetwork(); + for (int step = 0; step < 200; step++) + { + network = random.Next(5) == 0 + ? crossover.Cross(network, factory.NewNetwork(), random) + : mutation.Edits[random.Next(mutation.Edits.Count)].Edit.Mutate(network, random); + AssertWellFormed(network, factory.Space); + } + } + } + + [Fact] + public void DuplicateNeuronCopiesANeuronAndItsIncomingSynapses() + { + NetworkFactory factory = SmallFactory(7, inputCount: 0); + Network network = new Network(new[] + { + Neuron(2, new[] { 2 }, Standard("aa", 2)), + Neuron(0, new[] { 3 }, Standard("a", 1)), + OutputNeuron(0, Standard("a", 1)), + }); + + Network duplicated = Enumerable.Range(0, 20).Select(seed => new DuplicateNeuron(factory.Space).Mutate(network, new Random(seed))) + .First(candidate => candidate.Neurons[^1].Rules[0].Expression == "a" && candidate.Neurons[^1].Connections.SequenceEqual(new[] { 3 })); + + Assert.Equal(4, duplicated.Neurons.Count); + Assert.False(duplicated.Neurons[3].IsOutput); + Assert.Equal(new[] { 2, 4 }, duplicated.Neurons[0].Connections); + AssertWellFormed(duplicated, factory.Space with { InputCount = 0 }); + } + + [Fact] + public void DuplicationIsOnlyOfferedWhenAllowed() + { + NetworkFactory allowed = SmallFactory(1); + var notAllowed = Factories.Networks(allowed.Space with { DuplicateNeurons = false }, new Random(1), ExpressionGenerator.SimpleTemplates, expressions: new Random(1)); + + Assert.Contains(WeightedMutation.Structural(1, allowed).Edits, edit => edit.Edit is DuplicateNeuron); + Assert.DoesNotContain(WeightedMutation.Structural(1, notAllowed).Edits, edit => edit.Edit is DuplicateNeuron); + } + + [Fact] + public void NudgeExpressionChangesOneRunOfSpikesByOne() + { + Network network = new Network(new[] { OutputNeuron(0, new Rule("aa(aaa)*", 0, true)) }); + var seen = new HashSet(); + + for (int seed = 0; seed < 40; seed++) + { + seen.Add(new NudgeExpression().Mutate(network, new Random(seed)).Neurons[0].Rules[0].Expression); + } + + Assert.Equal(new HashSet { "a(aaa)*", "aaa(aaa)*", "aa(aa)*", "aa(aaaa)*" }, seen); + } + + [Fact] + public void SplitSynapseInsertsARelayInPlaceOfTheSynapse() + { + NetworkFactory factory = SmallFactory(0, inputCount: 0, form: RuleForm.Standard); + + Network split = new SplitSynapse(factory).Mutate(Identity(), new Random(0)); + + Assert.Equal(3, split.Neurons.Count); + Assert.Equal(new[] { 3 }, split.Neurons[0].Connections); + Assert.Equal(new[] { 2 }, split.Neurons[2].Connections); + Assert.Equal("a -> a", NetworkNotation.Rule(split.Neurons[2].Rules[0])); + } + + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/PartSearchTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/PartSearchTests.cs new file mode 100644 index 0000000..8e04258 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/PartSearchTests.cs @@ -0,0 +1,36 @@ +using SnpEvolution.Application; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Verification; + +namespace SnpEvolution.Tests.Search +{ + public class PartSearchTests + { + [Fact] + public void PartSearchUnderTheProfileFindsAndShrinksAProfilePart() + { + var settings = new PartSearchSettings(5_000, 1_000, 30, Catalog.StructuralDefault, () => new ExhaustiveCpuEngine(), HardwareProfile: true); + + PartOutcome outcome = PartSearch.Evolve(PartFixtures.DelayContract(2), 1, settings, new EvaluationBudget(), _ => { }); + + Assert.True(outcome.Solved); + Assert.Empty(HardwareProfile.Problems(outcome.Part!.Network)); + } + + [Fact] + [Slow] + public void ARobustShrinkKeepsAVerifiedPartAtLeastAsRobustAsTheOneFound() + { + var settings = new PartSearchSettings(5_000, 1_000, 30, Catalog.StructuralDefault, () => new ExhaustiveCpuEngine(), RobustJitter: 1); + + PartOutcome outcome = PartSearch.Evolve(PartFixtures.DelayContract(2), 1, settings, new EvaluationBudget(), _ => { }); + + Assert.True(outcome.Solved); + Assert.IsType(outcome.Measurement!.Verdict); + Assert.Equal(10, Robustness.Tenths(Robustness.Of(outcome.Part!, jitter: 1, budget: new EvaluationBudget(), runs: Robustness.CellRuns))); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/ProgramSearchTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/ProgramSearchTests.cs new file mode 100644 index 0000000..47a1262 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/ProgramSearchTests.cs @@ -0,0 +1,51 @@ +using SnpEvolution.Compilation; +using SnpEvolution.Search; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.Runs; + +namespace SnpEvolution.Tests.Search +{ + public class ProgramSearchTests + { + [Fact] + public void ProgramSearchFindsASmallSetAndItsNetworkGeneratesIt() + { + var search = new ProgramSearch(new ProgramSearchSettings(Population: 60)); + var target = new GeneratorTask("{2,3}", new[] { 2, 3 }, new JaccardFitness(new[] { 2, 3 })); + var budget = new EvaluationBudget(); + + SearchOutcome best = search.Run(new SearchRequest(target, budget, new Random(1), _ => { }) { MaxGenerations = 400 }); + + Assert.True(best.Solved, best.Best!.ToString()); + Assert.Equal(new[] { 2, 3 }, Generated(RegisterMachineCompiler.Compile(best.Best!))); + Assert.True(budget[EvaluationKind.InterpreterRun] > 0); + } + + [Fact] + public void ProgramSearchLearnsLargeSetsInStagesThatEndPastTheLargestNumber() + { + Assert.Equal(new long[] { 5, 13, 34, 89, 233, 610, 1976 }, ProgramScoring.StageBounds(new[] { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 })); + Assert.Equal(new long[] { 14 }, ProgramScoring.StageBounds(new[] { 2, 4, 6 })); + } + + [Fact] + public void AStageChecksEveryTargetNumberUpToItsBoundAndNothingExtra() + { + var scoring = new ProgramScoring(new[] { 1, 2, 3, 5, 8 }, 2_000, new EvaluationBudget()); + + ScoredProgram scored = scoring.Score(RegisterProgram.Parse("ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT")); + + Assert.Equal(5, scored.Checks.Count); + } + + [Fact] + public void AnEditNeverLeavesAProgramWithoutInstructions() + { + var edits = new ProgramEdits(16, 4, new Random(1)); + RegisterProgram single = RegisterProgram.Parse("HALT"); + + Assert.All(Enumerable.Range(0, 300), _ => Assert.NotEmpty(edits.Mutate(single).Instructions)); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ProposalTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Proposals/PartProposalsTests.cs similarity index 53% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ProposalTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/Proposals/PartProposalsTests.cs index 229f8f0..d6dc5d4 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ProposalTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Proposals/PartProposalsTests.cs @@ -1,74 +1,24 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Proposals; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Proposals; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; using Xunit.Abstractions; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Search.Proposals { - public class ProposalTests + public class PartProposalsTests { private readonly ITestOutputHelper output; - public ProposalTests(ITestOutputHelper output) => this.output = output; - - private static IReadOnlyList Parts(ShapeProposal? proposal) => proposal!.Contracts.Select(contract => contract.Name).Order().ToList(); - - [Fact] - public void FibonacciGapsAskForTwoRegistersAndAnAdd() => - Assert.Equal(new[] { "add", "register", "register" }, Parts(RecurrenceProposer.Propose(new[] { 1, 1, 2, 3, 5, 8, 13, 21 }))); - - [Fact] - public void PowersOfTwoAskForARegisterAndADouble() => - Assert.Equal(new[] { "double", "register" }, Parts(RecurrenceProposer.Propose(new[] { 1, 2, 4, 8, 16, 32 }))); - - [Fact] - [Slow] - public void ARandomSequenceAsksForNothing() - { - var random = new Random(31); - for (int attempt = 0; attempt < 20; attempt++) - { - int[] gaps = Enumerable.Range(0, 10).Select(_ => random.Next(1, 40)).ToArray(); - - Assert.Null(RecurrenceProposer.Propose(gaps)); - } - } - - // 2, 4, 6, 8 fits no linear recurrence with coefficients of 0 or more, but its gaps grow by 2 each time. - [Fact] - public void GapsWithAConstantDifferenceAskForARegisterAndAnAdd() - { - ShapeProposal proposal = RecurrenceProposer.Propose(new[] { 2, 4, 6, 8, 10 })!; - - Assert.Equal(new[] { "add", "register" }, Parts(proposal)); - Assert.Equal("every difference of the gaps is 2", proposal.Form); - Assert.Equal(new[] { "increment", "register" }, Parts(RecurrenceProposer.Propose(new[] { 3, 4, 5, 6, 7 }))); - Assert.Null(RecurrenceProposer.Propose(new[] { 10, 8, 6, 4, 2 })); - } - - [Fact] - public void AFailingGapProposesATimerAndFailingCasesASubContract() - { - var sequence = new SequenceTask("gaps", new[] { 2, 5, 3 }); - var task = new ContractTask(FirstParts.Named("increment")); - int caseThree = FirstParts.Values.ToList().IndexOf(3); - - Assert.Equal("delay 5", sequence.Propose(new[] { 1, 2 })!.Name); - Contract sub = task.Propose(new[] { ContractTask.CheckIndex(caseThree, ContractRule.DoneOnce) })!; - Assert.Equal("increment on n=3", sub.Name); - Assert.Equal(4, sub.Cases.Single().Outputs["out"]); - Assert.Null(task.Propose(Enumerable.Range(0, task.Contract.Cases.Count).Select(caseIndex => ContractTask.CheckIndex(caseIndex, ContractRule.OnTime)).ToList())); - } + public PartProposalsTests(ITestOutputHelper output) => this.output = output; // Fibonacci gaps fit a recurrence, so the target's shape asks for its parts before any check is read. [Fact] @@ -78,8 +28,7 @@ public void ASequenceTargetProposesThePartsItsShapeAsksFor() var library = new ModuleLibrary(); library.AddPart(Verified(ReferenceParts.Increment()), "a test"); var task = new SequenceTask("fibonacci", new[] { 1, 1, 2, 3, 5, 8 }); - var factory = new NetworkFactory(new GenomeSpace(InputCount: 0, RuleForm: RuleForm.Standard, MaxNeurons: 4), - new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); + var factory = Factories.Networks(new GenomeSpace(InputCount: 0, RuleForm: RuleForm.Standard, MaxNeurons: 4), random); var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(30, 2, OutputTiming.Interval), 1, random, new EvaluationBudget()); var space = new CompositionSpace(library, factory, new CompositionMix(), random); IGeneticAlgorithm search = SearchCatalog.CompositionMapElites.Create(new EvolutionContext(12, 0.5f, random, space.NewNetwork, evaluator, factory, _ => { }, Parts: library)); @@ -103,8 +52,7 @@ public void AStalledRunProposesAPartSolvesItAndAddsIt() var library = new ModuleLibrary(); library.AddPart(Verified(ReferenceParts.Increment()), "a test"); var task = new SequenceTask("gaps", new[] { 1, 6, 1 }); - var factory = new NetworkFactory(new GenomeSpace(InputCount: 0, RuleForm: RuleForm.Standard, MaxNeurons: 4), - new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); + var factory = Factories.Networks(new GenomeSpace(InputCount: 0, RuleForm: RuleForm.Standard, MaxNeurons: 4), random); var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(30, 2, OutputTiming.Interval), 1, random, new EvaluationBudget()); var space = new CompositionSpace(library, factory, new CompositionMix(), random); var context = new EvolutionContext(12, 0.5f, random, space.NewNetwork, evaluator, factory, _ => { }, Parts: library); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/Proposals/RecurrenceProposerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/Proposals/RecurrenceProposerTests.cs new file mode 100644 index 0000000..cf4a573 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/Proposals/RecurrenceProposerTests.cs @@ -0,0 +1,42 @@ +using SnpEvolution.Search.Proposals; + +namespace SnpEvolution.Tests.Search.Proposals +{ + public class RecurrenceProposerTests + { + private static IReadOnlyList Parts(ShapeProposal? proposal) => proposal!.Contracts.Select(contract => contract.Name).Order().ToList(); + + [Fact] + public void FibonacciGapsAskForTwoRegistersAndAnAdd() => + Assert.Equal(new[] { "add", "register", "register" }, Parts(RecurrenceProposer.Propose(new[] { 1, 1, 2, 3, 5, 8, 13, 21 }))); + + [Fact] + public void PowersOfTwoAskForARegisterAndADouble() => + Assert.Equal(new[] { "double", "register" }, Parts(RecurrenceProposer.Propose(new[] { 1, 2, 4, 8, 16, 32 }))); + + [Fact] + [Slow] + public void ARandomSequenceAsksForNothing() + { + var random = new Random(31); + for (int attempt = 0; attempt < 20; attempt++) + { + int[] gaps = Enumerable.Range(0, 10).Select(_ => random.Next(1, 40)).ToArray(); + + Assert.Null(RecurrenceProposer.Propose(gaps)); + } + } + + // 2, 4, 6, 8 fits no linear recurrence with coefficients of 0 or more, but its gaps grow by 2 each time. + [Fact] + public void GapsWithAConstantDifferenceAskForARegisterAndAnAdd() + { + ShapeProposal proposal = RecurrenceProposer.Propose(new[] { 2, 4, 6, 8, 10 })!; + + Assert.Equal(new[] { "add", "register" }, Parts(proposal)); + Assert.Equal("every difference of the gaps is 2", proposal.Form); + Assert.Equal(new[] { "increment", "register" }, Parts(RecurrenceProposer.Propose(new[] { 3, 4, 5, 6, 7 }))); + Assert.Null(RecurrenceProposer.Propose(new[] { 10, 8, 6, 4, 2 })); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/SearchCatalogTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/SearchCatalogTests.cs similarity index 86% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/SearchCatalogTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Search/SearchCatalogTests.cs index b8113b4..4f8a31a 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/SearchCatalogTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/SearchCatalogTests.cs @@ -1,13 +1,13 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; using SnpEvolution.Tests.Golden; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Search { - // The benchmark command runs in the shared console, so these belong with the golden runs. - [Collection(GoldenCollection.Name)] + // The benchmark command writes to the shared console. + [Collection(ProcessStateCollection.Name)] public class SearchCatalogTests { private sealed class RandomSampling : ISearch @@ -67,12 +67,6 @@ public void ABenchmarkOfCompositionSearchBuildsFromThePartLibrary() Assert.Contains("Composition search builds from", output); } - [Fact] - public void TheMenuOffersEveryGeneticAlgorithmInTheCatalog() - { - Assert.Equal(SearchCatalog.Evolution, SnpEvolution.Application.Catalog.Algorithms); - } - [Fact] public void TwoSearchesCannotShareAName() { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/ShrinkSearchTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/ShrinkSearchTests.cs new file mode 100644 index 0000000..7e73b51 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/ShrinkSearchTests.cs @@ -0,0 +1,54 @@ +using SnpEvolution.Compilation; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using static SnpEvolution.Tests.Fixtures.Runs; + +namespace SnpEvolution.Tests.Search +{ + public class ShrinkSearchTests + { + [Fact] + [Slow] + public void ShrinkingKeepsTheNetworkCorrectAndNeverGrowsIt() + { + int[] values = { 1, 2, 4, 8, 16, 32 }; + Network compiled = RecurrenceCompiler.Compile(Recurrence.Fit(values)!); + FitnessEvaluator evaluator = SequenceEvaluator(values); + var random = new Random(5); + var space = new GenomeSpace(RuleForm: RuleForm.Standard, MaxNeurons: compiled.Neurons.Count, MaxInitialSpikes: 8, MaxProduce: 2); + var factory = Factories.Networks(space, random, ExpressionGenerator.SimpleTemplates); + var setup = new NetworkSetup(20, 0.5f, factory, () => compiled, new NetworkScoring(() => new SequentialCpuEngine(), new SimulationOptions(50, 2, OutputTiming.Interval), 1)); + + SearchOutcome shrink = SearchCatalog.Shrink.Run(new SearchRequest(evaluator.Task, new EvaluationBudget(), random, _ => { }) + { + Seeds = new[] { new Individual(compiled) }, + MaxGenerations = 30, + Networks = setup, + }); + + Assert.Equal(30, shrink.Generations); + Assert.True(HardwareCost.Of(shrink.Best!.Genes).CompareTo(HardwareCost.Of(compiled)) <= 0); + Assert.True(evaluator.ConfirmSolved(shrink.Best!.Genes).Solved); + } + + [Fact] + public void ShrinkingUnderTheProfileNeverLeavesIt() + { + var random = new Random(4); + var factory = Factories.Networks(new GenomeSpace(InputCount: 1, MaxDelay: 3, HardwareProfile: true), random); + (var crossover, var edits) = ShrinkSearch.Operators(factory); + Network network = factory.NewNetwork(); + + for (int step = 0; step < 300; step++) + { + network = edits.Mutate(crossover.Cross(network, factory.NewNetwork(), random), random); + Assert.Empty(HardwareProfile.Problems(network)); + } + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Search/WeightedMutationTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Search/WeightedMutationTests.cs new file mode 100644 index 0000000..65ad976 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Search/WeightedMutationTests.cs @@ -0,0 +1,31 @@ +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Search +{ + public class WeightedMutationTests + { + [Fact] + public void PressureMutatesEveryChild() + { + var random = new Random(3); + NetworkFactory factory = Factories.StandardRules(random); + Network network = factory.NewNetwork(); + + Assert.Same(network, WeightedMutation.Structural(0, factory).Mutate(network, random)); + Assert.NotSame(network, WeightedMutation.Structural(0, factory, new MutationPressure { ExtraEdits = 3 }).Mutate(network, random)); + } + + [Fact] + public void WeightedMutationRespectsItsRate() + { + Network network = Identity(); + var mutation = new WeightedMutation(0, new[] { new WeightedEdit("nudge", new NudgeExpression(), 1) }); + + Assert.Same(network, mutation.Mutate(network, new Random(0))); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/AxonalDelayTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/AxonalDelayTests.cs index 0d3e0d5..0ceae67 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/AxonalDelayTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/AxonalDelayTests.cs @@ -1,17 +1,12 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; -using SnpEvolution.Storage; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.Runs; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Simulation { public class AxonalDelayTests { - private static Rule Axonal(string expression, int delay) => new Rule(expression, delay, true, axonal: true); - - private static NetworkSimulation Simulate(Network network) => - new NetworkSimulation(CompiledNetwork.Of(network), new Random(0), InputSpikes.None, OutputTiming.Interval); - [Fact] public void ConsumesAtOnceStaysOpenAndSendsDelayStepsLater() { @@ -89,21 +84,8 @@ public void AnAxonalRuleIsADifferentRuleFromTheSameRuleThatHoldsTheNeuron() var network = new Network(new[] { Neuron(0, new[] { 2 }, Axonal("a+", 2)), Neuron(0, Array.Empty(), new Rule("a+", 2, true)) }); Assert.NotEqual(network.Neurons[0].Rules[0].Key, network.Neurons[1].Rules[0].Key); - Assert.Equal(2, SnpEvolution.Evolution.Parts.HardwareCost.Of(network).DistinctRules); + Assert.Equal(2, SnpEvolution.Specs.Parts.HardwareCost.Of(network).DistinctRules); } - [Fact] - public void SavesTheAxonalFlagOnlyWhenSet() - { - var network = new Network(new[] { Neuron(1, new[] { 2 }, Axonal("a+", 2)), OutputNeuron(0, new Rule("a", 1, true)) }); - - string json = NetworkFiles.ToJson(network); - Network loaded = Assert.IsType(NetworkFiles.FromJson(json)); - - Assert.True(loaded.Neurons[0].Rules[0].Axonal); - Assert.False(loaded.Neurons[1].Rules[0].Axonal); - Assert.Single(json.Split("Axonal").Skip(1)); - Assert.Equal("a+ -> a;2 axonal", NetworkNotation.Rule(loaded.Neurons[0].Rules[0])); - } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/ExhaustiveEngineTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/ExhaustiveEngineTests.cs index a5476d3..79acf34 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/ExhaustiveEngineTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/ExhaustiveEngineTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Simulation { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/JitterTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/JitterTests.cs index ab7b69a..f8a9558 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/JitterTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/JitterTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Simulation { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/MetalEngineTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/Metal/MetalEngineTests.cs similarity index 91% rename from SNP_Evolution/SNP_Evolution.Tests/Simulation/MetalEngineTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Simulation/Metal/MetalEngineTests.cs index 02d534a..1da7fb7 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/MetalEngineTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/Metal/MetalEngineTests.cs @@ -1,24 +1,10 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; using SnpEvolution.Simulation.Metal; -using Xunit.Sdk; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; -namespace SnpEvolution.Tests.Simulation +namespace SnpEvolution.Tests.Simulation.Metal { - // Skipped on machines without a Metal GPU, and slow since it runs on the GPU. - [TraitDiscoverer(SpeedDiscoverer.TypeName, SpeedDiscoverer.AssemblyName)] - public sealed class MetalFactAttribute : FactAttribute, ITraitAttribute - { - public MetalFactAttribute() - { - if (!MetalEngine.IsAvailable) - { - Skip = "This machine has no Metal GPU."; - } - } - } - public class MetalEngineTests { private static readonly string[] Expressions = { "a", "aa", "a+", "a(aa)*", "aaa", "a(aa)+", "(aa)+" }; diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/NetworkRunnerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/NetworkRunnerTests.cs index 358567b..dad0b3e 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/NetworkRunnerTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/NetworkRunnerTests.cs @@ -1,4 +1,4 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; namespace SnpEvolution.Tests.Simulation diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/NetworkSimulationTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/NetworkSimulationTests.cs index e241654..6069943 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/NetworkSimulationTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/NetworkSimulationTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Simulation { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/PortReadoutTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/PortReadoutTests.cs index f02df3a..f2da3ed 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/PortReadoutTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/PortReadoutTests.cs @@ -1,7 +1,7 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; using SnpEvolution.Simulation.Metal; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Simulation { @@ -22,11 +22,8 @@ public class PortReadoutTests // sent it on step 3. private static Trial LoopTrial() => new Trial(Loop(), InputSpikes.None, Readout.Ports, new PortWatch(new[] { 2, 3 }, new[] { 3 }, StepsAfterDone: 1)); - public static TheoryData Engines => - new TheoryData { new SequentialCpuEngine(), new ParallelCpuEngine(), new ExhaustiveCpuEngine() }; - [Theory] - [MemberData(nameof(Engines))] + [MemberData(nameof(Engines.Cpu), MemberType = typeof(Engines))] public void ReportsEachWatchedNeuronsFiringsAndTheFinalSpikes(ISimulationEngine engine) { TrialResult result = engine.Run(new[] { LoopTrial() }, Options, new Random(1))[0]; @@ -43,7 +40,7 @@ public void ReportsEachWatchedNeuronsFiringsAndTheFinalSpikes(ISimulationEngine // The done neuron fires on steps 0 to 4, but the run stops one step after the first of them. // Neuron 3 holds the two spikes 2 sends it, the most any neuron holds. [Theory] - [MemberData(nameof(Engines))] + [MemberData(nameof(Engines.Cpu), MemberType = typeof(Engines))] public void RecordsTheMostSpikesAnyNeuronHeld(ISimulationEngine engine) { TrialResult result = engine.Run(new[] { LoopTrial() }, Options, new Random(1))[0]; @@ -53,7 +50,7 @@ public void RecordsTheMostSpikesAnyNeuronHeld(ISimulationEngine engine) // A shrunken network can lose the neuron a port is bound to; that port then never fires. [Theory] - [MemberData(nameof(Engines))] + [MemberData(nameof(Engines.Cpu), MemberType = typeof(Engines))] public void APortPastTheLastNeuronNeverFires(ISimulationEngine engine) { var trial = new Trial(Loop(), InputSpikes.None, Readout.Ports, new PortWatch(new[] { 3, 5 }, new[] { 5 }, StepsAfterDone: 1)); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/RoutedEngineTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/RoutedEngineTests.cs index 9a6e73e..5bf4093 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/RoutedEngineTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/RoutedEngineTests.cs @@ -1,5 +1,5 @@ using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Simulation { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/SimulationEngineTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/SimulationEngineTests.cs index 063572d..0f31885 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/SimulationEngineTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/SimulationEngineTests.cs @@ -1,4 +1,4 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; namespace SnpEvolution.Tests.Simulation @@ -10,10 +10,8 @@ public class SimulationEngineTests private static Network[] Batch() => new[] { ReferenceNetworks.NaturalNumbers(), TestNetworks.NeverOutputs(), ReferenceNetworks.EvenNumbers(), TestNetworks.AlwaysOutputsOne() }; - public static TheoryData Engines => new TheoryData { new SequentialCpuEngine(), new ParallelCpuEngine() }; - [Theory] - [MemberData(nameof(Engines))] + [MemberData(nameof(Engines.Sampling), MemberType = typeof(Engines))] public void ReturnsOneSortedOutputListPerNetworkInOrder(ISimulationEngine engine) { IReadOnlyList> outputs = engine.CollectOutputs(Batch(), Options, new Random(5)); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/SpikeTrainTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/SpikeTrainTests.cs index 13e9a4f..4461313 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/SpikeTrainTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/SpikeTrainTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Simulation { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/StandardSemanticsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Simulation/StandardSemanticsTests.cs index a2bb256..1dc2d6d 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/StandardSemanticsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Simulation/StandardSemanticsTests.cs @@ -1,14 +1,12 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.Runs; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Simulation { public class StandardSemanticsTests { - private static NetworkSimulation Simulate(Network network, InputSpikes? input = null, OutputTiming timing = OutputTiming.Interval) => - new NetworkSimulation(CompiledNetwork.Of(network), new Random(0), input ?? InputSpikes.None, timing); - [Fact] public void ConsumesExactlyItsSpikesAndSendsWhatItProduces() { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/EvaluationBudgetTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Accounting/EvaluationBudgetTests.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/EvaluationBudgetTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Accounting/EvaluationBudgetTests.cs index 18235c6..653eefc 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/EvaluationBudgetTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Accounting/EvaluationBudgetTests.cs @@ -1,13 +1,12 @@ using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Search; +using SnpEvolution.Search.Benchmarking; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Accounting { public class EvaluationBudgetTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/StatisticsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Accounting/StatisticsTests.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/StatisticsTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Accounting/StatisticsTests.cs index 137dce5..dbe54e5 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/StatisticsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Accounting/StatisticsTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Evolution.Accounting; +using SnpEvolution.Specs.Accounting; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Accounting { public class StatisticsTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ArithmeticPartsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/ArithmeticPartsTests.cs similarity index 86% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ArithmeticPartsTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/ArithmeticPartsTests.cs index b2164fc..037d046 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ArithmeticPartsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/ArithmeticPartsTests.cs @@ -1,18 +1,16 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Contracts { public class ArithmeticPartsTests { public static TheoryData ContractNames => new TheoryData(ArithmeticParts.Contracts.Concat(ArithmeticParts.BuildingBlocks).Select(contract => contract.Name)); - public static TheoryData HandBuilt => new TheoryData(Enumerable.Range(0, HandBuiltParts.All().Count)); - [Fact] public void FourOperationsInTwoEncodingsMakeEightContracts() { @@ -75,7 +73,7 @@ public void EveryArithmeticContractIsATaskInTheSuite() } [Theory] - [MemberData(nameof(HandBuilt))] + [MemberData(nameof(PartFixtures.HandBuilt), MemberType = typeof(PartFixtures))] public void EveryHandBuiltPartMeetsItsContract(int index) { Part part = HandBuiltParts.All()[index]; @@ -84,5 +82,11 @@ public void EveryHandBuiltPartMeetsItsContract(int index) Assert.True(measurement.Verdict is Verdict.Passed, $"{part.Contract.Name}: {measurement.Description}"); } + + [Fact] + public void EveryKnownContractFindsTheSpecificationItIsBuiltFrom() + { + Assert.All(ArithmeticParts.KnownEntries, entry => Assert.Same(entry.Specification, Specification.For(entry.Contract))); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ContractTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/ContractTests.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ContractTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/ContractTests.cs index a9beb81..ffc0c92 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ContractTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/ContractTests.cs @@ -1,7 +1,7 @@ -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Specs.Contracts; using SnpEvolution.Storage; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Contracts { public class ContractTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/FirstPartsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/FirstPartsTests.cs similarity index 80% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/FirstPartsTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/FirstPartsTests.cs index 6277261..5734d2a 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/FirstPartsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/FirstPartsTests.cs @@ -1,13 +1,11 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; -using SnpEvolution.Simulation; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; using SnpEvolution.Storage; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Contracts { public class FirstPartsTests { @@ -25,9 +23,6 @@ public class FirstPartsTests "| Zero test | count in; done-zero, done-nonzero | the right branch fires, the other never |\n" + "| Sequencer | done out xk | fires its outputs in order, each one step after the previous (k = 2, 3) |\n"; - private static FitnessResult Verify(ContractTask task, Network network) => - new FitnessEvaluator(new ExhaustiveCpuEngine(), task, new SimulationOptions(1, 5, OutputTiming.Interval), 1, new Random(0), new EvaluationBudget()).Evaluate(network); - public static TheoryData ContractNames => new TheoryData(FirstParts.Contracts.Select(contract => contract.Name)); [Fact] @@ -87,14 +82,14 @@ public void TheHandBuiltDelayMeetsItsFirstPartContract(int k) { Part delay = ReferenceParts.Delay(k); - Assert.Equal(1f, Verify(new ContractTask(FirstParts.Named($"delay {k}"), delay.Binding), delay.Network).Fitness); + Assert.Equal(1f, Runs.Evaluate(new ContractTask(FirstParts.Named($"delay {k}"), delay.Binding), delay.Network).Fitness); } [Fact] public void TheHandBuiltRegisterMeetsItsFirstPartContract() { Part register = ReferenceParts.Register(); - FitnessResult result = Verify(new ContractTask(FirstParts.Named("register"), register.Binding), register.Network); + FitnessResult result = Runs.Evaluate(new ContractTask(FirstParts.Named("register"), register.Binding), register.Network); Assert.True(result.Exact); Assert.Equal("meets the contract", result.Description); @@ -104,7 +99,7 @@ public void TheHandBuiltRegisterMeetsItsFirstPartContract() public void TheHandBuiltAddMeetsItsFirstPartContract() { Part add = ReferenceParts.Add(); - FitnessResult result = Verify(add.Task(), add.Network); + FitnessResult result = Runs.Evaluate(add.Task(), add.Network); Assert.True(result.Exact); Assert.Equal("meets the contract", result.Description); @@ -122,13 +117,13 @@ public void TheHandBuiltAddMeetsItsFirstPartContract() [Fact] public void ARelayChainMeetsTheSequencerContract() { - Assert.Equal("meets the contract", Verify(new ContractTask(FirstParts.Named("sequencer 2")), SequencerChain(swapped: false)).Description); + Assert.Equal("meets the contract", Runs.Evaluate(new ContractTask(FirstParts.Named("sequencer 2")), SequencerChain(swapped: false)).Description); } [Fact] public void OutputsFiringOutOfOrderFailOnlyDoneOnce() { - FitnessResult result = Verify(new ContractTask(FirstParts.Named("sequencer 2")), SequencerChain(swapped: true)); + FitnessResult result = Runs.Evaluate(new ContractTask(FirstParts.Named("sequencer 2")), SequencerChain(swapped: true)); Assert.Equal(new[] { 1f, 0f, 1f, 1f }, result.Checks); } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Simulation/PortEncodingTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/PortEncodingTests.cs similarity index 88% rename from SNP_Evolution/SNP_Evolution.Tests/Simulation/PortEncodingTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/PortEncodingTests.cs index e26d67e..bc5cb03 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Simulation/PortEncodingTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Contracts/PortEncodingTests.cs @@ -1,6 +1,7 @@ -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Tests.Simulation +namespace SnpEvolution.Tests.Specs.Contracts { public class PortEncodingTests { @@ -71,5 +72,12 @@ public void StartWaitsForTheQuietSteps() Assert.Equal(2, encoded.StartStep); Assert.Single(encoded.Input.StepsPerInput); } + + [Fact] + public void ANumberIsEncodedAsAnIntervalPortIs() + { + Assert.Equal(PortEncoding.Interval(5, 3), InputSpikes.Interval(5, 3)); + Assert.Equal(new[] { 0, 5 }, InputSpikes.Numbers(5).StepsPerInput[0]); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/CompositionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/CompositionTests.cs new file mode 100644 index 0000000..0643889 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/CompositionTests.cs @@ -0,0 +1,93 @@ +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Storage; +using static SnpEvolution.Tests.Fixtures.CompositionFixtures; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; + +namespace SnpEvolution.Tests.Specs.Parts +{ + public class CompositionTests + { + [Fact] + [Slow] + public void RandomCompositionsSurviveFlatteningAndRecovery() + { + ModuleLibrary library = Library(); + for (int seed = 0; seed < 200; seed++) + { + var random = new Random(seed); + Composition composition = RandomComposition.Of(library, GlueFactory(seed % 3, random), 1 + seed % 4, random); + + Network flat = composition.Flatten(library); + + Assert.InRange(composition.Glue.Count, 1, seed % 3 + 3); + + Assert.Equal(composition, Composition.Recover(flat, library)); + Assert.Equal(NetworkFiles.ToJson(flat), NetworkFiles.ToJson(Composition.Recover(flat, library)!.Flatten(library))); + } + } + + [Fact] + public void TheTwoIncrementChainIsACompositionThatFlattensToTheSameNetwork() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); + var first = new Network(increment.Part!.Part.Network.Neurons.Select(neuron => neuron.WithModule(new ModuleTag(increment.Id, 1))).ToList()); + Network chained = ModuleEdits.Insert(first, increment, 2, 24, library, new Random(1)); + PartCopy copy = PartWiring.Copies(chained, library).Single(each => each.Tag.Instance == 1); + + var composition = new Composition( + new[] { new PartInstance(1, increment.Id, 0), new PartInstance(2, increment.Id, 0) }, + Array.Empty(), + new[] { new PortWire(1, "out", 2, "n"), new PortWire(1, "done", 2, "start") }, + Array.Empty(), + new[] { new Endpoint(1, copy["start"]), new Endpoint(1, copy["n"]) }, + Array.Empty()); + + Assert.Equal(NetworkFiles.ToJson(chained), NetworkFiles.ToJson(composition.Flatten(library))); + Assert.Equal(composition, Composition.Recover(chained, library)); + } + + [Fact] + public void FlatteningTagsEveryPartNeuronWithItsInstance() + { + ModuleLibrary library = Library(); + Composition composition = RandomComposition.Of(library, GlueFactory(1, new Random(3)), 3, new Random(3)); + + Network flat = composition.Flatten(library); + + Assert.Equal(composition.Glue.Count, flat.Neurons.Count(neuron => neuron.Module == null)); + Assert.Equal( + composition.Parts.Select(part => new ModuleTag(part.Module, part.Instance)), + flat.Neurons.Where(neuron => neuron.Module != null).Select(neuron => neuron.Module!).Distinct()); + } + + [Fact] + public void ACopyWhoseInsideChangedIsNotRecovered() + { + ModuleLibrary library = Library(); + Network flat = RandomComposition.Of(library, GlueFactory(0, new Random(2)), 2, new Random(2)).Flatten(library); + int partNeuron = flat.Neurons.ToList().FindIndex(neuron => neuron.Module != null); + + Assert.Null(Composition.Recover(flat.WithNeuron(partNeuron, flat.Neurons[partNeuron].WithInitialSpikes(flat.Neurons[partNeuron].InitialSpikes + 1)), library)); + Assert.Null(Composition.Recover(flat.WithNeuron(partNeuron, flat.Neurons[partNeuron].WithRules(new[] { Rule.Standard("aaa", 3) })), library)); + } + + [Fact] + public void ALinkIntoAPartsOutPortDoesNotPassThroughItsPorts() + { + ModuleLibrary library = Library(); + Composition composition = RandomComposition.Of(library, GlueFactory(0, new Random(4)), 1, new Random(4)); + PartInstance part = composition.Parts[0]; + PartPort outPort = Composition.PartOf(part, library).Part.Ports().First(port => port.Port.Direction == PortDirection.Out); + Composition bypassing = composition with { Links = composition.Links.Append(new Link(Endpoint.GlueAt(1), new Endpoint(part.Instance, outPort.Position))).ToList() }; + + Assert.True(composition.ThroughPorts(library)); + Assert.False(bypassing.ThroughPorts(library)); + Assert.Equal(composition, bypassing.OnlyThroughPorts(library)); + } + + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/FibonacciCompositionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/FibonacciCompositionTests.cs similarity index 89% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/FibonacciCompositionTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/FibonacciCompositionTests.cs index 30df640..d5a5a07 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/FibonacciCompositionTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/FibonacciCompositionTests.cs @@ -1,20 +1,17 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; using Xunit.Abstractions; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Parts { - // Fibonacci built by hand from verified parts, typed wires and glue, as a size for composition search to beat; it stays a test because in the app it would be a seed by another name. + // Sequences.Fibonacci built by hand from verified parts, typed wires and glue, as a size for composition search to beat; it stays a test because in the app it would be a seed by another name. public class FibonacciCompositionTests { - private static readonly int[] Fibonacci = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584 }; - private readonly ITestOutputHelper output; public FibonacciCompositionTests(ITestOutputHelper output) => this.output = output; @@ -63,7 +60,7 @@ private static Machine Build() return new Machine(new Network(neurons), library, x1, y1, x2, y2, output, firstRound, relay1, relay2); } - private static string WireText(Machine machine, Wire wire) + private static string CopyNameWireText(Machine machine, Wire wire) { string Name(PartCopy copy) => copy.Tag == machine.X1.Tag ? "X1" : copy.Tag == machine.Y1.Tag ? "Y1" : copy.Tag == machine.X2.Tag ? "X2" : "Y2"; return $"{Name(wire.From.Copy)}.{wire.From.Port.Name}>{Name(wire.To.Copy)}.{wire.To.Port.Name}"; @@ -93,7 +90,7 @@ public void ItIsBuiltOnlyFromVerifiedLibraryPartsJoinedByTypedWiresAndGlue() "Y1.done>X2.start", "Y1.done>Y2.start", "Y1.sum>X2.n", "Y1.sum>Y2.a", "Y2.done>X1.start", "Y2.done>Y1.start", "Y2.sum>X1.n", "Y2.sum>Y1.a", }, - PartWiring.Wires(machine.Network, machine.Library).Select(wire => WireText(machine, wire)).Order()); + PartWiring.Wires(machine.Network, machine.Library).Select(wire => CopyNameWireText(machine, wire)).Order()); // Every synapse from one copy into another is one of those wires; the rest touch glue. Dictionary owner = copies.SelectMany(copy => copy.Positions.Select(position => (position, copy))).ToDictionary(pair => pair.position, pair => pair.copy); int betweenCopies = machine.Network.Neurons @@ -108,7 +105,7 @@ public void ItIsBuiltOnlyFromVerifiedLibraryPartsJoinedByTypedWiresAndGlue() public void ItMakesTheFirstSixteenFibonacciGapsExactlyOnTheExhaustiveEngine() { Network network = Build().Network; - var task = new SequenceTask("Fibonacci", Fibonacci.Take(16).ToList()); + var task = new SequenceTask("Sequences.Fibonacci", Sequences.Fibonacci.Take(16).ToList()); var evaluator = new FitnessEvaluator(new ExhaustiveCpuEngine(), task, new SimulationOptions(task.StepsNeeded, 20, OutputTiming.Interval), 1, new Random(1), new EvaluationBudget()); FitnessResult result = evaluator.Evaluate(network); @@ -121,9 +118,9 @@ public void ItMakesTheFirstSixteenFibonacciGapsExactlyOnTheExhaustiveEngine() [Fact] public void EachRoundLastsExactlyItsGapPastTheSixteenItIsScoredOn() { - List steps = OutputSteps(Build().Network, Fibonacci.Sum() + 10); + List steps = OutputSteps(Build().Network, Sequences.Fibonacci.Sum() + 10); - Assert.Equal(Fibonacci, steps.Zip(steps.Skip(1), (earlier, later) => later - earlier).Take(Fibonacci.Length)); + Assert.Equal(Sequences.Fibonacci, steps.Zip(steps.Skip(1), (earlier, later) => later - earlier).Take(Sequences.Fibonacci.Length)); output.WriteLine("Output spikes on steps " + string.Join(", ", steps)); } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/HardwareCostTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/HardwareCostTests.cs similarity index 86% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/HardwareCostTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/HardwareCostTests.cs index aaa9340..2cd5280 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/HardwareCostTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/HardwareCostTests.cs @@ -1,13 +1,13 @@ -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Parts { public class HardwareCostTests { @@ -79,9 +79,9 @@ public void CostsOrderByNeuronsSynapsesRulesThenRegisterWidth(HardwareCost small public void HardwareCostCanBeAMapElitesCell() { var random = new Random(1); - var factory = new NetworkFactory(new GenomeSpace(MaxNeurons: 5), new ExpressionGenerator(ExpressionGenerator.SimpleTemplates, 3, random), random); - var elites = new MapElites(10, random, factory.NewNetwork, new ConstantEvaluator(), new SnpEvolution.Evolution.Operators.NeuronCrossover(), - SnpEvolution.Evolution.Search.WeightedMutation.Structural(1, factory), cells: HardwareCost.Cell); + var factory = Factories.Networks(new GenomeSpace(MaxNeurons: 5), random, ExpressionGenerator.SimpleTemplates, length: 3); + var elites = new MapElites(10, random, factory.NewNetwork, new ConstantEvaluator(), new SnpEvolution.Search.Operators.NeuronCrossover(), + SnpEvolution.Search.WeightedMutation.Structural(1, factory), cells: HardwareCost.Cell); elites.NextGeneration(); elites.NextGeneration(); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleCutTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleCutTests.cs similarity index 86% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleCutTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleCutTests.cs index 018e03d..9efeec1 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleCutTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleCutTests.cs @@ -1,10 +1,9 @@ -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; -using static SnpEvolution.Tests.TestNetworks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Parts; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Parts { public class ModuleCutTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleEditTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleEditTests.cs similarity index 61% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleEditTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleEditTests.cs index f0e6fae..490e8b6 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ModuleEditTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleEditTests.cs @@ -1,16 +1,12 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; -using static SnpEvolution.Tests.Evolution.ModuleFixtures; -using static SnpEvolution.Tests.TestNetworks; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Parts { public class ModuleEditTests { @@ -96,36 +92,5 @@ public void ModuleTagsSurviveSavingAndAreShownInTheNotation() Assert.DoesNotContain("Module", NetworkFiles.ToJson(PingPong())); } - [Fact] - public void EditsWithModulesKeepNetworksWellFormedAndModulesWhole() - { - for (int seed = 0; seed < 20; seed++) - { - var random = new Random(seed); - var factory = new NetworkFactory(new GenomeSpace(InputCount: seed % 2, RuleForm: RuleForm.Standard, MaxNeurons: 12), - new ExpressionGenerator(ExpressionGenerator.ExperimentalTemplates, 4, random), random); - var library = new ModuleLibrary(); - library.Add(ModuleCuts.Whole(Chain()), "a test"); - library.Add(ModuleCuts.Cut(Chain(), new[] { 1 }), "a test"); - Part increment = seed % 2 == 0 ? ReferenceParts.Increment() : PartFixtures.PaddedIncrement(); - library.AddPart(Verifier.Measure(increment, new EvaluationBudget()).ToLibraryPart(increment, new PartOrigin(1, "a test", 0)), "a test"); - WeightedMutation mutation = WeightedMutation.Structural(1, factory, modules: new ModuleSupport(library)); - Network network = factory.NewNetwork(); - for (int step = 0; step < 150; step++) - { - WeightedEdit edit = mutation.Edits[random.Next(mutation.Edits.Count)]; - Network next = edit.Edit.Mutate(network, random); - Assert.True(edit.Edit is DissolveModule or SwapPart || ModuleEdits.KeepsModules(network, next), edit.Name); - network = next; - Assert.Single(network.Neurons, neuron => neuron.IsOutput); - Assert.InRange(network.Neurons.Count, 1, factory.Space.MaxNeurons); - for (int position = 1; position <= network.Neurons.Count; position++) - { - Assert.DoesNotContain(position, network.Neurons[position - 1].Connections); - Assert.All(network.Neurons[position - 1].Connections, target => Assert.InRange(target, 1, network.Neurons.Count)); - } - } - } - } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleLibraryTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleLibraryTests.cs new file mode 100644 index 0000000..b02d9b2 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/ModuleLibraryTests.cs @@ -0,0 +1,27 @@ +using SnpEvolution.Specs.Parts; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Specs.Parts +{ + public class ModuleLibraryTests + { + [Fact] + public void TheLibraryKeepsEachPartOnceAndDropsTheLeastHelpfulWhenFull() + { + var library = new ModuleLibrary(capacity: 2); + Module first = ModuleOf(library, Chain()); + Module second = ModuleOf(library, PingPong()); + + Assert.Same(first, ModuleOf(library, Chain())); + library.Credit(first.Id, improved: false); + library.Credit(second.Id, improved: true); + ModuleOf(library, AlwaysOutputsOne()); + + Assert.Equal(2, library.Modules.Count); + Assert.Null(library.Find(first.Id)); + Assert.Equal((1, 1), (second.Uses, second.Wins)); + } + + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/PartLibraryTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/PartLibraryTests.cs similarity index 67% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/PartLibraryTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/PartLibraryTests.cs index fb37d79..49519d2 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/PartLibraryTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/PartLibraryTests.cs @@ -1,16 +1,12 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Parts; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Parts { public class PartLibraryTests { private static readonly PartOrigin ByHand = new PartOrigin(0, "by hand", 0); - private static LibraryPart Measured(Part part) => Verifier.Measure(part, new EvaluationBudget()).ToLibraryPart(part, ByHand); - // The reference delay with a neuron that holds nothing and does nothing: the same behaviour at a higher cost. private static Part PaddedDelay(int k) { @@ -21,18 +17,18 @@ private static Part PaddedDelay(int k) [Fact] public void PartsThatReadTheSameOnEveryCaseHaveTheSameBehaviour() { - Assert.Equal(Measured(ReferenceParts.Delay(2)).Behaviour, Measured(PaddedDelay(2)).Behaviour); - Assert.NotEqual(Measured(ReferenceParts.Delay(2)).Behaviour, Measured(PartFixtures.DelayFiringDoneTwice(2)).Behaviour); + Assert.Equal(PartFixtures.Measured(ReferenceParts.Delay(2), ByHand).Behaviour, PartFixtures.Measured(PaddedDelay(2), ByHand).Behaviour); + Assert.NotEqual(PartFixtures.Measured(ReferenceParts.Delay(2), ByHand).Behaviour, PartFixtures.Measured(PartFixtures.DelayFiringDoneTwice(2), ByHand).Behaviour); } [Fact] public void AddingALargerPartWithTheSameBehaviourChangesNothing() { var library = new ModuleLibrary(); - LibraryPart small = Measured(ReferenceParts.Delay(2)); + LibraryPart small = PartFixtures.Measured(ReferenceParts.Delay(2), ByHand); Module kept = library.AddPart(small, "first"); - Module again = library.AddPart(Measured(PaddedDelay(2)), "second"); + Module again = library.AddPart(PartFixtures.Measured(PaddedDelay(2), ByHand), "second"); Assert.Same(kept, again); Assert.Same(small, again.Part); @@ -45,8 +41,8 @@ public void AddingASmallerPartReplacesItUnderTheSameId() { var log = new List(); var library = new ModuleLibrary(log: log.Add); - Module kept = library.AddPart(Measured(PaddedDelay(2)), "first"); - LibraryPart small = Measured(ReferenceParts.Delay(2)); + Module kept = library.AddPart(PartFixtures.Measured(PaddedDelay(2), ByHand), "first"); + LibraryPart small = PartFixtures.Measured(ReferenceParts.Delay(2), ByHand); Module replaced = library.AddPart(small, "second"); @@ -63,8 +59,8 @@ public void PartsWithDifferentResultsOnACaseAreBothKept() { var library = new ModuleLibrary(); - Module right = library.AddPart(Measured(ReferenceParts.Delay(2)), "first"); - Module firingTwice = library.AddPart(Measured(PartFixtures.DelayFiringDoneTwice(2)), "second"); + Module right = library.AddPart(PartFixtures.Measured(ReferenceParts.Delay(2), ByHand), "first"); + Module firingTwice = library.AddPart(PartFixtures.Measured(PartFixtures.DelayFiringDoneTwice(2), ByHand), "second"); Assert.NotEqual(right.Id, firingTwice.Id); Assert.Equal(2, library.Parts.Count); @@ -74,7 +70,7 @@ public void PartsWithDifferentResultsOnACaseAreBothKept() public void PartsAreNeverDroppedToMakeRoomForHarvestedModules() { var library = new ModuleLibrary(capacity: 1); - library.AddPart(Measured(ReferenceParts.Delay(2)), "a part"); + library.AddPart(PartFixtures.Measured(ReferenceParts.Delay(2), ByHand), "a part"); library.Add(ModuleCuts.Whole(ReferenceParts.Delay(3).Network), "harvested"); library.Add(ModuleCuts.Whole(ReferenceParts.Delay(4).Network), "harvested"); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/PartWiringTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/PartWiringTests.cs new file mode 100644 index 0000000..8f546d6 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/PartWiringTests.cs @@ -0,0 +1,131 @@ +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.PartWiringFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Specs.Parts +{ + public class PartWiringTests + { + // Evolved parts carry the output role on a port, which the reference increment does not. + private static Part IncrementWithOutput() + { + Part increment = ReferenceParts.Increment(); + return increment with { Network = increment.Network.WithNeuron(2, increment.Network.Neurons[2].WithRoles(true, false)) }; + } + + [Fact] + public void PartsShowTheirPortsAsNeuronPositions() + { + Part increment = ReferenceParts.Increment(); + + Assert.Equal(new[] { "start@1", "n@2", "out@3", "done@4" }, increment.Ports().Select(port => $"{port.Port.Name}@{port.Position}")); + } + + [Fact] + public void OnlyPortsOfTheSameKindAndWidthJoinOutToIn() + { + Port start = Port.In("start", PortKind.Trigger), done = Port.Out("done", PortKind.Trigger); + Port countIn = Port.In("n", PortKind.Count), countOut = Port.Out("out", PortKind.Count); + + Assert.True(PartWiring.Fits(done, start)); + Assert.True(PartWiring.Fits(countOut, countIn)); + Assert.False(PartWiring.Fits(start, done)); + Assert.False(PartWiring.Fits(countOut, start)); + Assert.False(PartWiring.Fits(Port.Out("out", PortKind.Interval), countIn)); + Assert.False(PartWiring.Fits(Port.Out("out", PortKind.Binary, 4), Port.In("n", PortKind.Binary, 5))); + Assert.True(PartWiring.Fits(Port.Out("out", PortKind.Binary, 4), Port.In("n", PortKind.Binary, 4))); + } + + [Fact] + public void TwoVerifiedIncrementsWiredByTypeAddTwoWithNoEvolution() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); + + for (int seed = 0; seed < 5; seed++) + { + Network network = Chained(library, increment, seed); + + Assert.Equal(new[] { "1.out>2.n", "1.done>2.start" }, PartWiring.Wires(network, library).Select(WireText)); + PartMeasurement measurement = MeasurePlusTwo(network, library); + Assert.True(measurement.Verdict is Verdict.Passed, measurement.Description); + } + } + + [Fact] + public void AThirdCopyIsFedByAnEarlierOneButNeverDoublesUpAnInPort() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); + + for (int seed = 0; seed < 10; seed++) + { + Network network = ModuleEdits.Insert(Chained(library, increment, seed), increment, 3, MaxNeurons, library, new Random(seed)); + + IReadOnlyList wires = PartWiring.Wires(network, library); + Assert.Equal(new[] { "n", "start" }, wires.Where(wire => wire.To.Copy.Tag.Instance == 3).Select(wire => wire.To.Port.Name).Order()); + Assert.All(wires.GroupBy(wire => wire.To.Position), group => Assert.Single(group)); + } + } + + [Fact] + public void APartInANetworkWithNoOtherPartIsWiredToUntypedNeurons() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); + + Network network = ModuleEdits.Insert(PingPong(), increment, 1, MaxNeurons, library, new Random(1)); + + PartCopy copy = PartWiring.Copies(network, library).Single(); + Assert.Contains(network.Neurons.Take(2), neuron => neuron.Connections.Contains(copy["start"])); + Assert.Contains(network.Neurons.Take(2), neuron => neuron.Connections.Contains(copy["n"])); + Assert.Empty(PartWiring.Wires(network, library)); + } + + [Fact] + public void APartWhoseInsideWasEditedIsNoLongerTyped() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(ReferenceParts.Increment()), "a test"); + Network network = Chained(library, increment, 1); + + Network edited = network.WithNeuron(4, network.Neurons[4].WithRules(new[] { Standard("a", 1) })); + + Assert.Single(PartWiring.Copies(edited, library)); + Assert.Empty(PartWiring.Wires(edited, library)); + } + + [Fact] + public void APortThatTakesTheOutputSendsNowhereAndAnOldOutputInACopyFeedsNoOtherPort() + { + var library = new ModuleLibrary(); + Module increment = library.AddPart(Verified(IncrementWithOutput()), "a test"); + int takeovers = 0; + + for (int seed = 0; seed < 20; seed++) + { + Network chained = Chained(library, increment, seed); + Network hosted = ModuleEdits.Insert(PingPong(), increment, 1, MaxNeurons, library, new Random(seed)); + + PartCopy first = PartWiring.Copies(chained, library).Single(copy => copy.Tag.Instance == 1); + PartCopy second = PartWiring.Copies(chained, library).Single(copy => copy.Tag.Instance == 2); + PartCopy guest = PartWiring.Copies(hosted, library).Single(); + if (chained.Neurons[second["out"] - 1].IsOutput) + { + takeovers++; + Assert.DoesNotContain(second["out"], chained.Neurons[first["out"] - 1].Connections); + } + if (hosted.Neurons[guest["out"] - 1].IsOutput) + { + Assert.Empty(hosted.Neurons[guest["out"] - 1].Connections); + } + } + Assert.InRange(takeovers, 1, 19); + } + + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/StopReasonTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/StopReasonTests.cs new file mode 100644 index 0000000..4be9501 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/StopReasonTests.cs @@ -0,0 +1,23 @@ +using SnpEvolution.Specs.Parts; + +namespace SnpEvolution.Tests.Specs.Parts +{ + public class StopReasonTests + { + // Part files keep the reason as text, so the files written before it was typed read back as the same reason. + [Theory] + [InlineData("every input checked")] + [InlineData("bound 6 reached")] + [InlineData("time limit of 10 s")] + [InlineData("n=3 has too many computations to follow exactly")] + [InlineData("counterexample at a=14,b=0,n=0")] + [InlineData("the contract has no specification")] + public void AStopReasonReadsBackFromItsText(string text) + { + StopReason reason = StopReason.Parse(text); + + Assert.NotEqual(Stop.Other, reason.Stop); + Assert.Equal(text, reason.ToString()); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/AcceptorTaskTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/AcceptorTaskTests.cs new file mode 100644 index 0000000..1330615 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/AcceptorTaskTests.cs @@ -0,0 +1,37 @@ +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.Runs; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Specs.Tasks +{ + public class AcceptorTaskTests + { + private static TrialResult Halts(bool halts) => new TrialResult(Array.Empty(), halts, TrialCoverage.Exact); + + [Fact] + public void AcceptorScoreIsBalancedAccuracy() + { + AcceptorTask task = AcceptorTask.Of("even", n => n % 2 == 0, new[] { 1, 2, 3, 4, 5, 6 }); + + Assert.Equal(1f, task.Score(new[] { false, true, false, true, false, true }.Select(Halts).ToList())); + Assert.Equal(0.5f, task.Score(Enumerable.Repeat(Halts(true), 6).ToList())); + Assert.Equal(0.5f, task.Score(Enumerable.Repeat(Halts(false), 6).ToList())); + Assert.Equal(0.5f, AcceptorTask.Of("big", n => n >= 4, new[] { 1, 2, 3, 4 }).Score(Enumerable.Repeat(Halts(true), 4).ToList())); + Assert.Equal(Readout.Halting, task.Cases[0].Readout); + } + + [Fact] + public void AcceptorThatAlwaysHaltsAcceptsEverything() + { + var network = new Network(new[] { InputNeuron(Array.Empty(), StandardForget("a", 1)), OutputNeuron(0, Standard("a", 1)) }); + + FitnessResult result = Runs.Evaluate(AcceptorTask.Of("big", n => n >= 3, Enumerable.Range(1, 4)), network, options: TaskOptions, solvedRetestCount: 3); + + Assert.Equal(0.5f, result.Fitness); + Assert.Equal("accepts {1,2,3,4}", result.Description); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ContractTaskTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/ContractTaskTests.cs similarity index 84% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/ContractTaskTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/ContractTaskTests.cs index 64222a7..ff5a9ed 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/ContractTaskTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/ContractTaskTests.cs @@ -1,22 +1,19 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Tasks { public class ContractTaskTests { - private static FitnessResult Verify(ContractTask task, Network network) => - new FitnessEvaluator(new ExhaustiveCpuEngine(), task, new SimulationOptions(1, 5, OutputTiming.Interval), 1, new Random(0), new EvaluationBudget()).Evaluate(network); - - private static FitnessResult Verify(Part part) => Verify(part.Task(), part.Network); + private static FitnessResult Verify(Part part) => Runs.Evaluate(part.Task(), part.Network); private static IEnumerable RuleChecks(FitnessResult result, ContractRule rule) => result.Checks!.Where((_, check) => check % ContractTask.RuleCount == (int)rule); @@ -68,7 +65,7 @@ public void ARegisterLeavingASpikeFailsOnlyBackToStart() [Fact] public void ADelayThatIsTooQuickFailsOnlyOnTime() { - FitnessResult result = Verify(new ContractTask(PartFixtures.DelayContract(3)), ReferenceParts.Delay(2).Network); + FitnessResult result = Runs.Evaluate(new ContractTask(PartFixtures.DelayContract(3)), ReferenceParts.Delay(2).Network); Assert.Equal(new[] { 1f, 1f, 1f, 0f }, result.Checks); } @@ -84,7 +81,7 @@ public void ANetworkThatNeverFiresDoneFailsDoneOnceAndOnTimeButIsQuiet() new Neuron(Array.Empty(), 0, Array.Empty(), false), }); - FitnessResult result = Verify(PartFixtures.Register(3).Task(), silent); + FitnessResult result = Runs.Evaluate(PartFixtures.Register(3).Task(), silent); Assert.All(RuleChecks(result, ContractRule.QuietBeforeStart), score => Assert.Equal(1f, score)); Assert.All(RuleChecks(result, ContractRule.DoneOnce), score => Assert.Equal(0f, score)); @@ -103,7 +100,7 @@ public void ACountThatIsCloseEarnsPartialCredit() .ToList(), }; - FitnessResult result = Verify(new ContractTask(offByOne, register.Binding), register.Network); + FitnessResult result = Runs.Evaluate(new ContractTask(offByOne, register.Binding), register.Network); Assert.All(RuleChecks(result, ContractRule.DoneOnce), score => Assert.InRange(score, 0.1f, 0.9f)); Assert.InRange(result.Fitness, 0.5f, 0.99f); @@ -211,10 +208,7 @@ public void RunsUnderTheExistingAlgorithms(string name) { var random = new Random(2); ContractTask task = ReferenceParts.Delay(2).Task(); - var factory = new NetworkFactory( - new GenomeSpace(InputCount: task.InputCount, RuleForm: RuleForm.Standard, MinNeurons: task.Binding.NeuronsNeeded), - new ExpressionGenerator(ExpressionGenerator.SimpleTemplates, 4, random), - random); + var factory = Factories.Networks(new GenomeSpace(InputCount: task.InputCount, RuleForm: RuleForm.Standard, MinNeurons: task.Binding.NeuronsNeeded), random, ExpressionGenerator.SimpleTemplates); var evaluator = new FitnessEvaluator(new ExhaustiveCpuEngine(), task, new SimulationOptions(1, 5, OutputTiming.Interval), 1, random, new EvaluationBudget()); IGeneticAlgorithm algorithm = SearchCatalog.Evolution.Single(search => search.Name == name) .Create(new EvolutionContext(12, 0.5f, random, factory.NewNetwork, evaluator, factory, _ => { })); @@ -227,5 +221,19 @@ public void RunsUnderTheExistingAlgorithms(string name) Assert.InRange(algorithm.Best!.Fitness, 0f, 1f); Assert.NotEmpty(algorithm.Population); } + + [Fact] + public void AFailingGapProposesATimerAndFailingCasesASubContract() + { + var sequence = new SequenceTask("gaps", new[] { 2, 5, 3 }); + var task = new ContractTask(FirstParts.Named("increment")); + int caseThree = FirstParts.Values.ToList().IndexOf(3); + + Assert.Equal("delay 5", sequence.Propose(new[] { 1, 2 })!.Name); + Contract sub = task.Propose(new[] { ContractTask.CheckIndex(caseThree, ContractRule.DoneOnce) })!; + Assert.Equal("increment on n=3", sub.Name); + Assert.Equal(4, sub.Cases.Single().Outputs["out"]); + Assert.Null(task.Propose(Enumerable.Range(0, task.Contract.Cases.Count).Select(caseIndex => ContractTask.CheckIndex(caseIndex, ContractRule.OnTime)).ToList())); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/CurriculumPlanTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/CurriculumPlanTests.cs new file mode 100644 index 0000000..d453f91 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/CurriculumPlanTests.cs @@ -0,0 +1,22 @@ +using SnpEvolution.Specs.Tasks; + +namespace SnpEvolution.Tests.Specs.Tasks +{ + public class CurriculumPlanTests + { + [Fact] + public void CurriculumStagesEndWithTheWholeTarget() + { + Assert.Equal(new[] { 3, 4, 5, 6 }, new CurriculumPlan(3, 1).Lengths(6)); + Assert.Equal(new[] { 8, 12, 15 }, new CurriculumPlan(8, 4).Lengths(15)); + Assert.Equal(new[] { 4 }, new CurriculumPlan(10, 2).Lengths(4)); + } + + [Fact] + public void BinaryWordsStartWithLongerStagesThanSequences() + { + Assert.Equal(new CurriculumPlan(8, 4), ((IPrefixTask)new SpikeWordTask("word", Enumerable.Repeat(true, 20).ToList())).Curriculum); + Assert.Equal(new CurriculumPlan(3, 1), ((IPrefixTask)new SequenceTask("gaps", new[] { 1, 2, 3, 4, 5 })).Curriculum); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/FitnessFunctionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/FitnessFunctionTests.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/FitnessFunctionTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/FitnessFunctionTests.cs index 074a470..a4933fe 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/FitnessFunctionTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/FitnessFunctionTests.cs @@ -1,6 +1,6 @@ -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Tasks { public class FitnessFunctionTests { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/FunctionTaskTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/FunctionTaskTests.cs new file mode 100644 index 0000000..1cac831 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/FunctionTaskTests.cs @@ -0,0 +1,50 @@ +using SnpEvolution.Search.Fitness; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.Runs; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Specs.Tasks +{ + public class FunctionTaskTests + { + private static TrialResult Outputs(params int[] outputs) => new TrialResult(outputs, false, TrialCoverage.Exact); + + [Fact] + public void IdentityNetworkSolvesTheIdentityFunctionExactly() + { + FitnessResult result = Runs.Evaluate(FunctionTask.Of("n", n => n, Enumerable.Range(1, 6)), Identity(), options: TaskOptions, solvedRetestCount: 3); + + Assert.Equal(1f, result.Fitness); + Assert.True(result.Exact); + Assert.Contains("f(3)={3}/3", result.Description); + } + + [Fact] + public void IdentityNetworkIsCloseButWrongForTheSuccessor() + { + float fitness = Runs.Evaluate(FunctionTask.Of("n + 1", n => n + 1, Enumerable.Range(1, 6)), Identity(), options: TaskOptions, solvedRetestCount: 3).Fitness; + + Assert.Equal(0.25f, fitness, precision: 5); + } + + [Fact] + public void FunctionScoreGivesFullCreditOnlyForTheRightOutput() + { + var task = new FunctionTask("f", new[] { new FunctionExample(new[] { 2 }, 4), new FunctionExample(new[] { 3 }, 6) }); + + Assert.Equal(1f, task.Score(new[] { Outputs(4), Outputs(6) })); + Assert.Equal(0.5f, task.Score(new[] { Outputs(4), Outputs() })); + Assert.Equal(0.78125f, task.Score(new[] { Outputs(4, 4), Outputs(6, 9) }), precision: 5); + } + + [Fact] + public void TwoArgumentFunctionsNeedTwoInputs() + { + FunctionTask task = FunctionTask.Of("add", (first, second) => first + second, new[] { (1, 2) }); + + Assert.Equal(2, task.InputCount); + Assert.Equal(new[] { 0, 2 }, task.Cases[0].Input.StepsPerInput[1]); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/GeneratorTaskTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/GeneratorTaskTests.cs new file mode 100644 index 0000000..3752a7a --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/GeneratorTaskTests.cs @@ -0,0 +1,22 @@ +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.Runs; +using static SnpEvolution.Tests.Fixtures.TestNetworks; + +namespace SnpEvolution.Tests.Specs.Tasks +{ + public class GeneratorTaskTests + { + [Fact] + public void GeneratorTaskScoresWithItsFitnessFunction() + { + var task = new GeneratorTask("{1}", new[] { 1 }, new JaccardFitness(new[] { 1 })); + + FitnessResult result = Runs.Evaluate(task, AlwaysOutputsOne(), options: TaskOptions, solvedRetestCount: 3); + + Assert.Equal(1f, result.Fitness); + Assert.Equal("{1}", result.Description); + Assert.Equal(new[] { 0f, 1f, 0f }, new GeneratorTask("{1,2,3}", new[] { 1, 2, 3 }, new JaccardFitness(new[] { 1, 2, 3 })).Checks(new[] { 2 })); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SequenceTaskTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SequenceTaskTests.cs new file mode 100644 index 0000000..d5ccd48 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SequenceTaskTests.cs @@ -0,0 +1,101 @@ +using SnpEvolution.Search.Fitness; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.ModuleFixtures; +using static SnpEvolution.Tests.Fixtures.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TrialResults; + +namespace SnpEvolution.Tests.Specs.Tasks +{ + public class SequenceTaskTests + { + [Fact] + public void SequenceScoreRewardsTheRightGapsInOrder() + { + var task = new SequenceTask("fib", new[] { 1, 1, 2, 3 }); + + Assert.Equal(1f, task.Score(new[] { Trains(new[] { 4, 5, 6, 8, 11, 20 }) })); + Assert.Equal(0.5f, task.Score(new[] { Trains(new[] { 0, 1, 2 }) })); + // Half the runs right and half silent: the mean is a half and the worst run nothing, so a quarter. + Assert.Equal(0.25f, task.Score(new[] { Trains(new[] { 0, 1, 2, 4, 7 }, Array.Empty()) })); + } + + [Fact] + public void SequenceScoreStopsAtTheFirstWrongGap() + { + var task = new SequenceTask("fib", new[] { 1, 1, 2, 3 }); + + // Gaps 1,2,2,3: one right, then 2 for 1 earns a quarter, and the 2,3 after the mistake earn nothing. + Assert.Equal(0.3125f, task.Score(new[] { Trains(new[] { 0, 1, 3, 5, 8 }) }), precision: 5); + } + + [Fact] + public void SequenceNicheIsTheRightPrefixByTheLongestGap() + { + var task = new SequenceTask("fib", new[] { 1, 1, 2, 3, 5 }); + + Assert.Equal((3, 3), task.Niche(new[] { Trains(new[] { 0, 1, 2, 4, 9 }) })); + Assert.Equal((0, 0), task.Niche(new[] { Trains(new[] { 4 }) })); + } + + [Fact] + public void PrefixesKeepTheOpeningValues() + { + var sequence = new SequenceTask("fib", new[] { 1, 1, 2, 3, 5 }); + var word = new SpikeWordTask("w", new[] { true, false, true, true }); + + Assert.Equal(new[] { 1, 1, 2 }, ((SequenceTask)sequence.Prefix(3)).Expected); + Assert.Equal(new[] { true, false }, ((SpikeWordTask)word.Prefix(2)).Expected); + Assert.True(sequence.Prefix(3).StepsNeeded < sequence.StepsNeeded); + } + + [Fact] + public void SequenceGivesPartialCreditForCloseGaps() + { + var task = new SequenceTask("twos", new[] { 2, 2 }); + + Assert.Equal(0.625f, task.Score(new[] { Trains(new[] { 0, 2, 5 }) }), precision: 5); + } + + [Fact] + public void RunsAreLengthenedToFitTheWholeTarget() + { + FitnessResult result = Runs.Evaluate(new SequenceTask("twos", Enumerable.Repeat(2, 20).ToList()), PingPong(), new ParallelCpuEngine(), new SimulationOptions(5, 5, OutputTiming.Interval), solvedRetestCount: 3); + + Assert.Equal(1f, result.Fitness); + Assert.StartsWith("intervals [2,2,2", result.Description); + } + + [Fact] + public void SequenceChecksScoreEachGapInPlaceEvenAfterAMistake() + { + var task = new SequenceTask("test", new[] { 2, 1, 2 }); + var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(20, 2, OutputTiming.Interval), 1, new Random(1), new EvaluationBudget()); + + FitnessResult result = evaluator.Evaluate(PingPong()); + + Assert.Equal(new[] { 1f, 0f, 1f }, result.Checks); + Assert.Equal("gap 2 (1)", task.CheckName(1)); + Assert.Equal(new[] { 2, 1, 2 }, ((SequenceTask)task.Focus(1)!).Expected); + } + + [Fact] + public void ATriggeredPartCountsItsGapsFromTheTrigger() + { + var task = (TriggeredSequenceTask)new SequenceTask("test", new[] { 1, 2, 2, 2, 7 }).Triggered(1)!; + var evaluator = new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(30, 2, OutputTiming.Interval), 1, new Random(1), new EvaluationBudget()); + + FitnessResult result = evaluator.Evaluate(TriggeredTwos()); + Cut cut = ModuleCuts.Whole(TriggeredTwos()); + + Assert.Equal(new[] { 2, 2, 2 }, task.Expected); + Assert.Equal(1, task.InputCount); + Assert.Equal(new[] { 1f, 1f, 1f }, result.Checks); + Assert.True(Solved.Solves(result.Fitness)); + Assert.Equal(3, cut.Body.Neurons.Count); + Assert.Equal(new[] { 0 }, cut.Inputs); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SolvedTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SolvedTests.cs new file mode 100644 index 0000000..e93a4a3 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SolvedTests.cs @@ -0,0 +1,18 @@ +using SnpEvolution.Specs.Tasks; + +namespace SnpEvolution.Tests.Specs.Tasks +{ + public class SolvedTests + { + [Theory] + [InlineData(0.985f, true)] + [InlineData(1f, true)] + [InlineData(0.984f, false)] + [InlineData(1.01f, false)] + [InlineData(float.NaN, false)] + public void SolvingFitnessIsBetweenTheThresholdAndOne(float fitness, bool solving) + { + Assert.Equal(solving, Solved.Solves(fitness)); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SpikeWordTaskTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SpikeWordTaskTests.cs new file mode 100644 index 0000000..a17fea2 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/SpikeWordTaskTests.cs @@ -0,0 +1,39 @@ +using SnpEvolution.Search.Fitness; +using SnpEvolution.Simulation; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; +using static SnpEvolution.Tests.Fixtures.TrialResults; + +namespace SnpEvolution.Tests.Specs.Tasks +{ + public class SpikeWordTaskTests + { + [Fact] + public void SpikeWordNicheIsTheRightPrefixBySpikeCount() + { + var task = new SpikeWordTask("w", new[] { true, false, true, true }); + + // Fires on steps 0 and 2: right for three steps, with two spikes, as shares of 16 and 8 buckets. + Assert.Equal((12, 4), task.Niche(new[] { Trains(new[] { 0, 2 }) })); + } + + [Fact] + public void BinaryWordScoreIsBalancedAccuracy() + { + var task = new SpikeWordTask("w", new[] { false, true, false, true }); + + Assert.Equal(1f, task.Score(new[] { Trains(new[] { 1, 3, 9 }) })); + Assert.Equal(0.5f, task.Score(new[] { Trains(Array.Empty()) })); + Assert.Equal(0.5f, task.Score(new[] { Trains(new[] { 0, 1, 2, 3 }) })); + } + + [Fact] + public void PingPongSpellsAlternatingBits() + { + FitnessResult result = Runs.Evaluate(new SpikeWordTask("01", Enumerable.Range(0, 12).Select(step => step % 2 == 1).ToList()), PingPong(), new ParallelCpuEngine(), new SimulationOptions(3, 5, OutputTiming.Interval), solvedRetestCount: 3); + + Assert.Equal(1f, result.Fitness); + Assert.Equal("spikes 010101010101 / 010101010101", result.Description); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/StreamingTaskTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/StreamingTaskTests.cs similarity index 73% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/StreamingTaskTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/StreamingTaskTests.cs index 1328bc9..e23b4c7 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/StreamingTaskTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/StreamingTaskTests.cs @@ -1,22 +1,17 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Benchmarking; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Search.Fitness; using SnpEvolution.Simulation; -using static SnpEvolution.Tests.TestNetworks; +using SnpEvolution.Specs.Tasks; +using static SnpEvolution.Tests.Fixtures.TestNetworks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Tasks { public class StreamingTaskTests { private static readonly StreamingTask Debouncer = StreamingTask.Debouncer(spikes: 2, within: 3); private static readonly StreamingTask RateDetector = StreamingTask.RateDetector(spikes: 3, within: 6); - private static FitnessResult Evaluate(ITask task, Network network) => - new FitnessEvaluator(new SequentialCpuEngine(), task, new SimulationOptions(0, 5, OutputTiming.Interval), solvedRetestCount: 3, new Random(0), new EvaluationBudget()) - .Evaluate(network); - // The input and two relays bring each input spike to the counter on three steps running, so the counter holds // the input's last three steps. It forgets one spike and fires on two or three. private static Neuron[] WindowOfThree(int counterTargets) => new[] @@ -54,7 +49,7 @@ public void EveryDebouncerCaseHasBurstsAndGlitches() [Fact] public void HandBuiltDebouncerSolvesItsTask() { - FitnessResult result = Evaluate(Debouncer, HandBuiltDebouncer()); + FitnessResult result = Runs.Evaluate(Debouncer, HandBuiltDebouncer(), new SequentialCpuEngine(), new SimulationOptions(0, 5, OutputTiming.Interval), solvedRetestCount: 3); Assert.Equal(1f, result.Fitness); Assert.All(result.Checks!, check => Assert.Equal(1f, check)); @@ -63,20 +58,20 @@ public void HandBuiltDebouncerSolvesItsTask() [Fact] public void CopyingTheInputScoresBelowAHalf() { - Assert.InRange(Evaluate(Debouncer, Identity()).Fitness, 0f, 0.49f); + Assert.InRange(Runs.Evaluate(Debouncer, Identity(), new SequentialCpuEngine(), new SimulationOptions(0, 5, OutputTiming.Interval), solvedRetestCount: 3).Fitness, 0f, 0.49f); } [Fact] public void SilenceScoresAHalf() { - Assert.Equal(0.5f, Evaluate(Debouncer, NeverOutputs()).Fitness); - Assert.Equal(0.5f, Evaluate(RateDetector, NeverOutputs()).Fitness); + Assert.Equal(0.5f, Runs.Evaluate(Debouncer, NeverOutputs(), new SequentialCpuEngine(), new SimulationOptions(0, 5, OutputTiming.Interval), solvedRetestCount: 3).Fitness); + Assert.Equal(0.5f, Runs.Evaluate(RateDetector, NeverOutputs(), new SequentialCpuEngine(), new SimulationOptions(0, 5, OutputTiming.Interval), solvedRetestCount: 3).Fitness); } [Fact] public void DebouncerWithoutRefractoryAnswersSomeBurstsTwice() { - float fitness = Evaluate(Debouncer, HandBuiltRateDetector()).Fitness; + float fitness = Runs.Evaluate(Debouncer, HandBuiltRateDetector(), new SequentialCpuEngine(), new SimulationOptions(0, 5, OutputTiming.Interval), solvedRetestCount: 3).Fitness; Assert.InRange(fitness, 0.5f, 0.99f); } @@ -84,7 +79,7 @@ public void DebouncerWithoutRefractoryAnswersSomeBurstsTwice() [Fact] public void HandBuiltRateDetectorSolvesItsTask() { - Assert.Equal(1f, Evaluate(RateDetector, HandBuiltRateDetector()).Fitness); + Assert.Equal(1f, Runs.Evaluate(RateDetector, HandBuiltRateDetector(), new SequentialCpuEngine(), new SimulationOptions(0, 5, OutputTiming.Interval), solvedRetestCount: 3).Fitness); } [Fact] diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/TaskScoringTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/TaskScoringTests.cs similarity index 65% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/TaskScoringTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/TaskScoringTests.cs index 566156d..2d9bdd9 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/TaskScoringTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Tasks/TaskScoringTests.cs @@ -1,8 +1,6 @@ -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Simulation; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Tasks { public class TaskScoringTests { @@ -40,18 +38,5 @@ public void TheCorrectPrefixStopsAtTheFirstMistakeOrTheShorterList() Assert.Equal(2, TaskScoring.CorrectPrefix(new[] { 1, 2, 3 }, new[] { 1, 2 })); } - [Fact] - public void ANumberIsEncodedAsAnIntervalPortIs() - { - Assert.Equal(PortEncoding.Interval(5, 3), InputSpikes.Interval(5, 3)); - Assert.Equal(new[] { 0, 5 }, InputSpikes.Numbers(5).StepsPerInput[0]); - } - - [Fact] - public void BinaryWordsStartWithLongerStagesThanSequences() - { - Assert.Equal(new CurriculumPlan(8, 4), ((IPrefixTask)new SpikeWordTask("word", Enumerable.Repeat(true, 20).ToList())).Curriculum); - Assert.Equal(new CurriculumPlan(3, 1), ((IPrefixTask)new SequenceTask("gaps", new[] { 1, 2, 3, 4, 5 })).Curriculum); - } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/BoundedCheckTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Verification/BoundedCheckTests.cs similarity index 69% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/BoundedCheckTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Verification/BoundedCheckTests.cs index 6d9fded..c839422 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/BoundedCheckTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Verification/BoundedCheckTests.cs @@ -1,35 +1,18 @@ using System.Diagnostics; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; using SnpEvolution.Storage; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Verification { public class BoundedCheckTests { private static readonly ProofLimits AMinute = new ProofLimits(TimeSpan.FromMinutes(1)); - // The hand-built register, except that a store holding 41 spikes (n = 20 and start) sends two spikes instead of one. - internal static Part RegisterFailingAtTwenty() - { - Part register = HandBuiltParts.All().Single(part => part.Contract.Name == "register"); - const int Store = 4; - List neurons = register.Network.Neurons.ToList(); - neurons[Store] = new Neuron( - new[] { Rule.Standard("a(aa){1,19}|a(aa){21,}", 2), Rule.Standard("a{41}", 2, 2), Rule.Standard("a", 1, 2) }, 0, neurons[Store].Connections, false); - return register with { Network = new Network(neurons) }; - } - - [Fact] - public void EveryKnownContractFindsTheSpecificationItIsBuiltFrom() - { - Assert.All(ArithmeticParts.KnownEntries, entry => Assert.Same(entry.Specification, Specification.For(entry.Contract))); - } - [Fact] [Slow] @@ -61,7 +44,7 @@ public void ProvesTheRegisterReferencePartToAtLeast32WithinAMinute() [Slow] public void CatchesAPartThatFailsOnlyAtTwentyWithItsTrace() { - Part broken = RegisterFailingAtTwenty(); + Part broken = PartFixtures.RegisterFailingAtTwenty(); Assert.IsType(Verifier.Measure(broken, new EvaluationBudget()).Verdict); var budget = new EvaluationBudget(); @@ -102,7 +85,7 @@ public void APartIsNotAdmittedWhenItFailsWithinTwiceItsLargestCase() { var log = new List(); - Assert.IsType(BoundedCheck.Admit(RegisterFailingAtTwenty(), new EvaluationBudget(), log.Add).Verdict); + Assert.IsType(BoundedCheck.Admit(PartFixtures.RegisterFailingAtTwenty(), new EvaluationBudget(), log.Add).Verdict); Assert.Contains(log, line => line.Contains("Not admitted") && line.Contains("n=20")); Assert.Equal(24, BoundedCheck.Admission(FirstParts.Named("register")).MaxBound); } @@ -150,40 +133,12 @@ public void InputsAtABoundHaveThatLargestValueAndIntervalsStartAtOne() Assert.Equal(new[] { 1 }, BoundedInputs.WithLargest(FirstParts.Named("interval to count"), 1).Select(inputs => inputs["n"])); } - [Fact] - public void ALibraryFileKeepsItsProvenBound() - { - Part delay = ReferenceParts.Delay(2); - LibraryPart part = Verifier.Measure(delay, new EvaluationBudget()).ToLibraryPart(delay, new PartOrigin(0, "by hand", 0)) with { Proven = new ProvenBound(0, true, new StopReason(Stop.EveryInputChecked)) }; - - LibraryPart read = PartLibraryFiles.Read(PartLibraryFiles.ToJson(part), "delay-2.json"); - - Assert.Equal(part.Proven, read.Proven); - Assert.Contains("\"Stopped\": \"every input checked\"", PartLibraryFiles.ToJson(part)); - } - - // Part files keep the reason as text, so the files written before it was typed read back as the same reason. - [Theory] - [InlineData("every input checked")] - [InlineData("bound 6 reached")] - [InlineData("time limit of 10 s")] - [InlineData("n=3 has too many computations to follow exactly")] - [InlineData("counterexample at a=14,b=0,n=0")] - [InlineData("the contract has no specification")] - public void AStopReasonReadsBackFromItsText(string text) - { - StopReason reason = StopReason.Parse(text); - - Assert.NotEqual(Stop.Other, reason.Stop); - Assert.Equal(text, reason.ToString()); - } - // CEGIS adds a counterexample's case to the cases a search scores on. [Fact] [Slow] public void ACounterexampleIsACaseAContractCanTake() { - Part broken = RegisterFailingAtTwenty(); + Part broken = PartFixtures.RegisterFailingAtTwenty(); Counterexample counterexample = Assert.IsType(BoundedCheck.Prove(broken, AMinute, new EvaluationBudget()).Verdict).Counterexample; Contract withIt = broken.Contract with { Cases = broken.Contract.Cases.Append(counterexample.Case).ToList(), MaxLatency = counterexample.Contract.MaxLatency }; @@ -195,8 +150,8 @@ public void ACounterexampleIsACaseAContractCanTake() [Slow] public void ALibraryFileWithACounterexampleIsRefused() { - Part broken = RegisterFailingAtTwenty(); - LibraryPart part = Verifier.Measure(broken, new EvaluationBudget()).ToLibraryPart(broken, new PartOrigin(0, "by hand", 0)) with { Proven = BoundedCheck.Prove(broken, AMinute, new EvaluationBudget()).Proven }; + Part broken = PartFixtures.RegisterFailingAtTwenty(); + LibraryPart part = PartFixtures.Measured(broken, new PartOrigin(0, "by hand", 0)) with { Proven = BoundedCheck.Prove(broken, AMinute, new EvaluationBudget()).Proven }; var refusal = Assert.Throws(() => PartLibraryFiles.Read(PartLibraryFiles.ToJson(part), "register.json")); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Evolution/RobustnessTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Specs/Verification/RobustnessTests.cs similarity index 61% rename from SNP_Evolution/SNP_Evolution.Tests/Evolution/RobustnessTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Specs/Verification/RobustnessTests.cs index a8100f9..9bd5a20 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Evolution/RobustnessTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Specs/Verification/RobustnessTests.cs @@ -1,18 +1,13 @@ -using SnpEvolution.Application; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; -namespace SnpEvolution.Tests.Evolution +namespace SnpEvolution.Tests.Specs.Verification { public class RobustnessTests { - public static IEnumerable HandBuilt => Enumerable.Range(0, HandBuiltParts.All().Count).Select(index => new object[] { index }); - [Theory] - [MemberData(nameof(HandBuilt))] + [MemberData(nameof(PartFixtures.HandBuilt), MemberType = typeof(PartFixtures))] public void EveryHandBuiltPartIsRobustWithoutJitter(int index) { Assert.Equal(1f, Robustness.Of(HandBuiltParts.All()[index], jitter: 0, budget: new EvaluationBudget(), runs: 10)); @@ -42,19 +37,6 @@ public void TheSameSeedGivesTheSameScore() Assert.Equal(0, budget.Networks); } - [Fact] - [Slow] - public void ARobustShrinkKeepsAVerifiedPartAtLeastAsRobustAsTheOneFound() - { - var settings = new PartSearchSettings(5_000, 1_000, 30, Catalog.StructuralDefault, () => new ExhaustiveCpuEngine(), RobustJitter: 1); - - PartOutcome outcome = PartSearch.Evolve(PartFixtures.DelayContract(2), 1, settings, new EvaluationBudget(), _ => { }); - - Assert.True(outcome.Solved); - Assert.IsType(outcome.Measurement!.Verdict); - Assert.Equal(10, Robustness.Tenths(Robustness.Of(outcome.Part!, jitter: 1, budget: new EvaluationBudget(), runs: Robustness.CellRuns))); - } - [Fact] public void CellsAreRobustnessInTenthsByNeurons() { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Storage/NetworkFilesTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Storage/NetworkFilesTests.cs index 4ab2022..61f86e5 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Storage/NetworkFilesTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Storage/NetworkFilesTests.cs @@ -1,5 +1,6 @@ -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Storage; +using static SnpEvolution.Tests.Fixtures.TestNetworks; namespace SnpEvolution.Tests.Storage { @@ -66,5 +67,39 @@ public void RejectsAMissingFile() { Assert.Null(NetworkFiles.Load(Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".json"))); } + + [Fact] + public void StandardRulesAndInputNeuronsRoundTripThroughJson() + { + var network = new Network(new[] + { + InputNeuron(new[] { 2 }, Standard("a(aa)*", 1, produce: 2, delay: 1)), + OutputNeuron(0, new Rule("a", 0, true)), + }); + + string json = NetworkFiles.ToJson(network); + Network loaded = Assert.IsType(NetworkFiles.FromJson(json)); + + Rule rule = loaded.Neurons[0].Rules[0]; + Assert.Equal((1L, 2, 1), (rule.Consume!.Value, rule.Produce, rule.Delay)); + Assert.True(loaded.Neurons[0].IsInput); + Assert.Null(loaded.Neurons[1].Rules[0].Consume); + Assert.DoesNotContain("\"Size\"", json); + Assert.Equal(json, NetworkFiles.ToJson(loaded)); + } + + [Fact] + public void SavesTheAxonalFlagOnlyWhenSet() + { + var network = new Network(new[] { Neuron(1, new[] { 2 }, Axonal("a+", 2)), OutputNeuron(0, new Rule("a", 1, true)) }); + + string json = NetworkFiles.ToJson(network); + Network loaded = Assert.IsType(NetworkFiles.FromJson(json)); + + Assert.True(loaded.Neurons[0].Rules[0].Axonal); + Assert.False(loaded.Neurons[1].Rules[0].Axonal); + Assert.Single(json.Split("Axonal").Skip(1)); + Assert.Equal("a+ -> a;2 axonal", NetworkNotation.Rule(loaded.Neurons[0].Rules[0])); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Storage/PartLibraryFilesTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Storage/PartLibraryFilesTests.cs index 0e1e3eb..3e37161 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Storage/PartLibraryFilesTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Storage/PartLibraryFilesTests.cs @@ -1,26 +1,19 @@ -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; namespace SnpEvolution.Tests.Storage { public sealed class PartLibraryFilesTests : IDisposable { - private readonly string folder = Path.Combine(Path.GetTempPath(), "part-library-" + Guid.NewGuid().ToString("N")); + private readonly TempFolder temp = new TempFolder("part-library"); - public void Dispose() - { - if (Directory.Exists(folder)) - { - Directory.Delete(folder, recursive: true); - } - } + private string folder => temp.Path; + + public void Dispose() => temp.Dispose(); - private static LibraryPart Measured(Part part, int seed = 7) => - Verifier.Measure(part, new EvaluationBudget()).ToLibraryPart(part, new PartOrigin(seed, "evolve-parts --seed 1", 1234)); + private static readonly PartOrigin Evolved = new PartOrigin(7, "evolve-parts --seed 1", 1234); private static Part FirstPartRegister() { @@ -33,7 +26,7 @@ private static ModuleLibrary Library(params Part[] parts) var library = new ModuleLibrary(); foreach (Part part in parts) { - library.AddPart(Measured(part), "a test"); + library.AddPart(PartFixtures.Measured(part, Evolved), "a test"); } return library; } @@ -79,7 +72,7 @@ public void SavingTwiceWritesTheSameBytes() public void TheNetworkIsWrittenInTheNetworkFileFormat() { Part delay = ReferenceParts.Delay(2); - string json = PartLibraryFiles.ToJson(Measured(delay)); + string json = PartLibraryFiles.ToJson(PartFixtures.Measured(delay, Evolved)); string network = Newtonsoft.Json.Linq.JObject.Parse(json)["Network"]!.ToString(); @@ -115,5 +108,17 @@ public void APartFileThatChangesAFirstPartContractIsRefused() [Fact] public void AMissingFolderLoadsAsAnEmptyLibrary() => Assert.Empty(PartLibraryFiles.Load(folder).Parts); + + [Fact] + public void ALibraryFileKeepsItsProvenBound() + { + Part delay = ReferenceParts.Delay(2); + LibraryPart part = PartFixtures.Measured(delay, new PartOrigin(0, "by hand", 0)) with { Proven = new ProvenBound(0, true, new StopReason(Stop.EveryInputChecked)) }; + + LibraryPart read = PartLibraryFiles.Read(PartLibraryFiles.ToJson(part), "delay-2.json"); + + Assert.Equal(part.Proven, read.Proven); + Assert.Contains("\"Stopped\": \"every input checked\"", PartLibraryFiles.ToJson(part)); + } } } diff --git a/SNP_Evolution/SNP_Evolution.slnx b/SNP_Evolution/SNP_Evolution.slnx index 2f413c6..d1bd080 100644 --- a/SNP_Evolution/SNP_Evolution.slnx +++ b/SNP_Evolution/SNP_Evolution.slnx @@ -1,4 +1,12 @@ + + + + + + + + + - diff --git a/SNP_Evolution/SNP_Evolution/SNP_Evolution.csproj b/SNP_Evolution/SNP_Evolution/SNP_Evolution.csproj deleted file mode 100644 index 860f2b5..0000000 --- a/SNP_Evolution/SNP_Evolution/SNP_Evolution.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - Exe - SNP_Evolution - SnpEvolution - 1.0.0 - true - snp-evolution - SNP_Evolution - $(MSBuildThisFileDirectory)../nupkg - true - - - - - - - - - - - - - - - diff --git a/SNP_Evolution/SNP_Evolution/obj/SNP_Evolution.csproj.nuget.g.props b/SNP_Evolution/SNP_Evolution/obj/SNP_Evolution.csproj.nuget.g.props deleted file mode 100644 index 30752da..0000000 --- a/SNP_Evolution/SNP_Evolution/obj/SNP_Evolution.csproj.nuget.g.props +++ /dev/null @@ -1,15 +0,0 @@ - - - - True - NuGet - $(MSBuildThisFileDirectory)project.assets.json - /Users/michaelstachowicz/.nuget/packages/ - /Users/michaelstachowicz/.nuget/packages/ - PackageReference - 7.0.0 - - - - - \ No newline at end of file diff --git a/SNP_Evolution/SNP_Evolution/obj/SNP_Evolution.csproj.nuget.g.targets b/SNP_Evolution/SNP_Evolution/obj/SNP_Evolution.csproj.nuget.g.targets deleted file mode 100644 index 3dc06ef..0000000 --- a/SNP_Evolution/SNP_Evolution/obj/SNP_Evolution.csproj.nuget.g.targets +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/SNP_Evolution/SNP_Evolution/obj/project.assets.json b/SNP_Evolution/SNP_Evolution/obj/project.assets.json deleted file mode 100644 index 72b1be3..0000000 --- a/SNP_Evolution/SNP_Evolution/obj/project.assets.json +++ /dev/null @@ -1,402 +0,0 @@ -{ - "version": 4, - "targets": { - "net10.0": { - "Newtonsoft.Json/13.0.3": { - "type": "package", - "compile": { - "lib/net6.0/Newtonsoft.Json.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net6.0/Newtonsoft.Json.dll": { - "related": ".xml" - } - } - } - } - }, - "libraries": { - "Newtonsoft.Json/13.0.3": { - "sha512": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==", - "type": "package", - "path": "newtonsoft.json/13.0.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.md", - "README.md", - "lib/net20/Newtonsoft.Json.dll", - "lib/net20/Newtonsoft.Json.xml", - "lib/net35/Newtonsoft.Json.dll", - "lib/net35/Newtonsoft.Json.xml", - "lib/net40/Newtonsoft.Json.dll", - "lib/net40/Newtonsoft.Json.xml", - "lib/net45/Newtonsoft.Json.dll", - "lib/net45/Newtonsoft.Json.xml", - "lib/net6.0/Newtonsoft.Json.dll", - "lib/net6.0/Newtonsoft.Json.xml", - "lib/netstandard1.0/Newtonsoft.Json.dll", - "lib/netstandard1.0/Newtonsoft.Json.xml", - "lib/netstandard1.3/Newtonsoft.Json.dll", - "lib/netstandard1.3/Newtonsoft.Json.xml", - "lib/netstandard2.0/Newtonsoft.Json.dll", - "lib/netstandard2.0/Newtonsoft.Json.xml", - "newtonsoft.json.13.0.3.nupkg.sha512", - "newtonsoft.json.nuspec", - "packageIcon.png" - ] - } - }, - "projectFileDependencyGroups": { - "net10.0": [ - "Newtonsoft.Json >= 13.0.3" - ] - }, - "packageFolders": { - "/Users/michaelstachowicz/.nuget/packages/": {} - }, - "project": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "/Users/michaelstachowicz/SNP_Evolution/SNP_Evolution/SNP_Evolution/SNP_Evolution.csproj", - "projectName": "SNP_Evolution", - "projectPath": "/Users/michaelstachowicz/SNP_Evolution/SNP_Evolution/SNP_Evolution/SNP_Evolution.csproj", - "packagesPath": "/Users/michaelstachowicz/.nuget/packages/", - "outputPath": "/Users/michaelstachowicz/SNP_Evolution/SNP_Evolution/SNP_Evolution/obj/", - "projectStyle": "PackageReference", - "configFilePaths": [ - "/Users/michaelstachowicz/.nuget/NuGet/NuGet.Config" - ], - "originalTargetFrameworks": [ - "net10.0" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "net10.0": { - "framework": "net10.0", - "targetAlias": "net10.0", - "projectReferences": {} - } - }, - "warningProperties": { - "allWarningsAsErrors": true, - "warnAsError": [ - "NU1605" - ] - }, - "restoreAuditProperties": { - "enableAudit": "true", - "auditLevel": "low", - "auditMode": "all" - }, - "SdkAnalysisLevel": "10.0.300" - }, - "frameworks": { - "net10.0": { - "framework": "net10.0", - "targetAlias": "net10.0", - "dependencies": { - "Newtonsoft.Json": { - "target": "Package", - "version": "[13.0.3, )" - } - }, - "imports": [ - "net461", - "net462", - "net47", - "net471", - "net472", - "net48", - "net481" - ], - "assetTargetFallback": true, - "warn": true, - "frameworkReferences": { - "Microsoft.NETCore.App": { - "privateAssets": "all" - } - }, - "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/10.0.302/PortableRuntimeIdentifierGraph.json", - "packagesToPrune": { - "Microsoft.CSharp": "(,4.7.32767]", - "Microsoft.VisualBasic": "(,10.4.32767]", - "Microsoft.Win32.Primitives": "(,4.3.32767]", - "Microsoft.Win32.Registry": "(,5.0.32767]", - "runtime.any.System.Collections": "(,4.3.32767]", - "runtime.any.System.Diagnostics.Tools": "(,4.3.32767]", - "runtime.any.System.Diagnostics.Tracing": "(,4.3.32767]", - "runtime.any.System.Globalization": "(,4.3.32767]", - "runtime.any.System.Globalization.Calendars": "(,4.3.32767]", - "runtime.any.System.IO": "(,4.3.32767]", - "runtime.any.System.Reflection": "(,4.3.32767]", - "runtime.any.System.Reflection.Extensions": "(,4.3.32767]", - "runtime.any.System.Reflection.Primitives": "(,4.3.32767]", - "runtime.any.System.Resources.ResourceManager": "(,4.3.32767]", - "runtime.any.System.Runtime": "(,4.3.32767]", - "runtime.any.System.Runtime.Handles": "(,4.3.32767]", - "runtime.any.System.Runtime.InteropServices": "(,4.3.32767]", - "runtime.any.System.Text.Encoding": "(,4.3.32767]", - "runtime.any.System.Text.Encoding.Extensions": "(,4.3.32767]", - "runtime.any.System.Threading.Tasks": "(,4.3.32767]", - "runtime.any.System.Threading.Timer": "(,4.3.32767]", - "runtime.aot.System.Collections": "(,4.3.32767]", - "runtime.aot.System.Diagnostics.Tools": "(,4.3.32767]", - "runtime.aot.System.Diagnostics.Tracing": "(,4.3.32767]", - "runtime.aot.System.Globalization": "(,4.3.32767]", - "runtime.aot.System.Globalization.Calendars": "(,4.3.32767]", - "runtime.aot.System.IO": "(,4.3.32767]", - "runtime.aot.System.Reflection": "(,4.3.32767]", - "runtime.aot.System.Reflection.Extensions": "(,4.3.32767]", - "runtime.aot.System.Reflection.Primitives": "(,4.3.32767]", - "runtime.aot.System.Resources.ResourceManager": "(,4.3.32767]", - "runtime.aot.System.Runtime": "(,4.3.32767]", - "runtime.aot.System.Runtime.Handles": "(,4.3.32767]", - "runtime.aot.System.Runtime.InteropServices": "(,4.3.32767]", - "runtime.aot.System.Text.Encoding": "(,4.3.32767]", - "runtime.aot.System.Text.Encoding.Extensions": "(,4.3.32767]", - "runtime.aot.System.Threading.Tasks": "(,4.3.32767]", - "runtime.aot.System.Threading.Timer": "(,4.3.32767]", - "runtime.debian.8-x64.runtime.native.System": "(,4.3.32767]", - "runtime.debian.8-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.debian.8-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.debian.8-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.debian.9-x64.runtime.native.System": "(,4.3.32767]", - "runtime.debian.9-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.debian.9-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.debian.9-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.fedora.23-x64.runtime.native.System": "(,4.3.32767]", - "runtime.fedora.23-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.fedora.23-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.fedora.23-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.fedora.24-x64.runtime.native.System": "(,4.3.32767]", - "runtime.fedora.24-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.fedora.24-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.fedora.24-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.fedora.27-x64.runtime.native.System": "(,4.3.32767]", - "runtime.fedora.27-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.fedora.27-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.fedora.27-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.fedora.28-x64.runtime.native.System": "(,4.3.32767]", - "runtime.fedora.28-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.fedora.28-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.fedora.28-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.opensuse.13.2-x64.runtime.native.System": "(,4.3.32767]", - "runtime.opensuse.13.2-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.opensuse.13.2-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.opensuse.13.2-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.opensuse.42.1-x64.runtime.native.System": "(,4.3.32767]", - "runtime.opensuse.42.1-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.opensuse.42.1-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.opensuse.42.1-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.opensuse.42.3-x64.runtime.native.System": "(,4.3.32767]", - "runtime.opensuse.42.3-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.opensuse.42.3-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.opensuse.42.3-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.osx.10.10-x64.runtime.native.System": "(,4.3.32767]", - "runtime.osx.10.10-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.osx.10.10-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.osx.10.10-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "(,4.3.32767]", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.rhel.7-x64.runtime.native.System": "(,4.3.32767]", - "runtime.rhel.7-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.rhel.7-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.rhel.7-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.ubuntu.14.04-x64.runtime.native.System": "(,4.3.32767]", - "runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.ubuntu.16.04-x64.runtime.native.System": "(,4.3.32767]", - "runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.ubuntu.16.10-x64.runtime.native.System": "(,4.3.32767]", - "runtime.ubuntu.16.10-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.ubuntu.16.10-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.ubuntu.16.10-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]", - "runtime.ubuntu.18.04-x64.runtime.native.System": "(,4.3.32767]", - "runtime.ubuntu.18.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.ubuntu.18.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]", - "runtime.ubuntu.18.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]", - "runtime.unix.Microsoft.Win32.Primitives": "(,4.3.32767]", - "runtime.unix.System.Console": "(,4.3.32767]", - "runtime.unix.System.Diagnostics.Debug": "(,4.3.32767]", - "runtime.unix.System.IO.FileSystem": "(,4.3.32767]", - "runtime.unix.System.Net.Primitives": "(,4.3.32767]", - "runtime.unix.System.Net.Sockets": "(,4.3.32767]", - "runtime.unix.System.Private.Uri": "(,4.3.32767]", - "runtime.unix.System.Runtime.Extensions": "(,4.3.32767]", - "runtime.win.Microsoft.Win32.Primitives": "(,4.3.32767]", - "runtime.win.System.Console": "(,4.3.32767]", - "runtime.win.System.Diagnostics.Debug": "(,4.3.32767]", - "runtime.win.System.IO.FileSystem": "(,4.3.32767]", - "runtime.win.System.Net.Primitives": "(,4.3.32767]", - "runtime.win.System.Net.Sockets": "(,4.3.32767]", - "runtime.win.System.Runtime.Extensions": "(,4.3.32767]", - "runtime.win10-arm-aot.runtime.native.System.IO.Compression": "(,4.0.32767]", - "runtime.win10-arm64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.win10-x64-aot.runtime.native.System.IO.Compression": "(,4.0.32767]", - "runtime.win10-x86-aot.runtime.native.System.IO.Compression": "(,4.0.32767]", - "runtime.win7-x64.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.win7-x86.runtime.native.System.IO.Compression": "(,4.3.32767]", - "runtime.win7.System.Private.Uri": "(,4.3.32767]", - "runtime.win8-arm.runtime.native.System.IO.Compression": "(,4.3.32767]", - "System.AppContext": "(,4.3.32767]", - "System.Buffers": "(,5.0.32767]", - "System.Collections": "(,4.3.32767]", - "System.Collections.Concurrent": "(,4.3.32767]", - "System.Collections.Immutable": "(,10.0.32767]", - "System.Collections.NonGeneric": "(,4.3.32767]", - "System.Collections.Specialized": "(,4.3.32767]", - "System.ComponentModel": "(,4.3.32767]", - "System.ComponentModel.Annotations": "(,4.3.32767]", - "System.ComponentModel.EventBasedAsync": "(,4.3.32767]", - "System.ComponentModel.Primitives": "(,4.3.32767]", - "System.ComponentModel.TypeConverter": "(,4.3.32767]", - "System.Console": "(,4.3.32767]", - "System.Data.Common": "(,4.3.32767]", - "System.Data.DataSetExtensions": "(,4.4.32767]", - "System.Diagnostics.Contracts": "(,4.3.32767]", - "System.Diagnostics.Debug": "(,4.3.32767]", - "System.Diagnostics.DiagnosticSource": "(,10.0.32767]", - "System.Diagnostics.FileVersionInfo": "(,4.3.32767]", - "System.Diagnostics.Process": "(,4.3.32767]", - "System.Diagnostics.StackTrace": "(,4.3.32767]", - "System.Diagnostics.TextWriterTraceListener": "(,4.3.32767]", - "System.Diagnostics.Tools": "(,4.3.32767]", - "System.Diagnostics.TraceSource": "(,4.3.32767]", - "System.Diagnostics.Tracing": "(,4.3.32767]", - "System.Drawing.Primitives": "(,4.3.32767]", - "System.Dynamic.Runtime": "(,4.3.32767]", - "System.Formats.Asn1": "(,10.0.32767]", - "System.Formats.Tar": "(,10.0.32767]", - "System.Globalization": "(,4.3.32767]", - "System.Globalization.Calendars": "(,4.3.32767]", - "System.Globalization.Extensions": "(,4.3.32767]", - "System.IO": "(,4.3.32767]", - "System.IO.Compression": "(,4.3.32767]", - "System.IO.Compression.ZipFile": "(,4.3.32767]", - "System.IO.FileSystem": "(,4.3.32767]", - "System.IO.FileSystem.AccessControl": "(,4.4.32767]", - "System.IO.FileSystem.DriveInfo": "(,4.3.32767]", - "System.IO.FileSystem.Primitives": "(,4.3.32767]", - "System.IO.FileSystem.Watcher": "(,4.3.32767]", - "System.IO.IsolatedStorage": "(,4.3.32767]", - "System.IO.MemoryMappedFiles": "(,4.3.32767]", - "System.IO.Pipelines": "(,10.0.32767]", - "System.IO.Pipes": "(,4.3.32767]", - "System.IO.Pipes.AccessControl": "(,5.0.32767]", - "System.IO.UnmanagedMemoryStream": "(,4.3.32767]", - "System.Linq": "(,4.3.32767]", - "System.Linq.AsyncEnumerable": "(,10.0.32767]", - "System.Linq.Expressions": "(,4.3.32767]", - "System.Linq.Parallel": "(,4.3.32767]", - "System.Linq.Queryable": "(,4.3.32767]", - "System.Memory": "(,5.0.32767]", - "System.Net.Http": "(,4.3.32767]", - "System.Net.Http.Json": "(,10.0.32767]", - "System.Net.NameResolution": "(,4.3.32767]", - "System.Net.NetworkInformation": "(,4.3.32767]", - "System.Net.Ping": "(,4.3.32767]", - "System.Net.Primitives": "(,4.3.32767]", - "System.Net.Requests": "(,4.3.32767]", - "System.Net.Security": "(,4.3.32767]", - "System.Net.ServerSentEvents": "(,10.0.32767]", - "System.Net.Sockets": "(,4.3.32767]", - "System.Net.WebHeaderCollection": "(,4.3.32767]", - "System.Net.WebSockets": "(,4.3.32767]", - "System.Net.WebSockets.Client": "(,4.3.32767]", - "System.Numerics.Vectors": "(,5.0.32767]", - "System.ObjectModel": "(,4.3.32767]", - "System.Private.DataContractSerialization": "(,4.3.32767]", - "System.Private.Uri": "(,4.3.32767]", - "System.Reflection": "(,4.3.32767]", - "System.Reflection.DispatchProxy": "(,6.0.32767]", - "System.Reflection.Emit": "(,4.7.32767]", - "System.Reflection.Emit.ILGeneration": "(,4.7.32767]", - "System.Reflection.Emit.Lightweight": "(,4.7.32767]", - "System.Reflection.Extensions": "(,4.3.32767]", - "System.Reflection.Metadata": "(,10.0.32767]", - "System.Reflection.Primitives": "(,4.3.32767]", - "System.Reflection.TypeExtensions": "(,4.3.32767]", - "System.Resources.Reader": "(,4.3.32767]", - "System.Resources.ResourceManager": "(,4.3.32767]", - "System.Resources.Writer": "(,4.3.32767]", - "System.Runtime": "(,4.3.32767]", - "System.Runtime.CompilerServices.Unsafe": "(,7.0.32767]", - "System.Runtime.CompilerServices.VisualC": "(,4.3.32767]", - "System.Runtime.Extensions": "(,4.3.32767]", - "System.Runtime.Handles": "(,4.3.32767]", - "System.Runtime.InteropServices": "(,4.3.32767]", - "System.Runtime.InteropServices.RuntimeInformation": "(,4.3.32767]", - "System.Runtime.Loader": "(,4.3.32767]", - "System.Runtime.Numerics": "(,4.3.32767]", - "System.Runtime.Serialization.Formatters": "(,4.3.32767]", - "System.Runtime.Serialization.Json": "(,4.3.32767]", - "System.Runtime.Serialization.Primitives": "(,4.3.32767]", - "System.Runtime.Serialization.Xml": "(,4.3.32767]", - "System.Security.AccessControl": "(,6.0.32767]", - "System.Security.Claims": "(,4.3.32767]", - "System.Security.Cryptography.Algorithms": "(,4.3.32767]", - "System.Security.Cryptography.Cng": "(,5.0.32767]", - "System.Security.Cryptography.Csp": "(,4.3.32767]", - "System.Security.Cryptography.Encoding": "(,4.3.32767]", - "System.Security.Cryptography.OpenSsl": "(,5.0.32767]", - "System.Security.Cryptography.Primitives": "(,4.3.32767]", - "System.Security.Cryptography.X509Certificates": "(,4.3.32767]", - "System.Security.Principal": "(,4.3.32767]", - "System.Security.Principal.Windows": "(,5.0.32767]", - "System.Security.SecureString": "(,4.3.32767]", - "System.Text.Encoding": "(,4.3.32767]", - "System.Text.Encoding.CodePages": "(,10.0.32767]", - "System.Text.Encoding.Extensions": "(,4.3.32767]", - "System.Text.Encodings.Web": "(,10.0.32767]", - "System.Text.Json": "(,10.0.32767]", - "System.Text.RegularExpressions": "(,4.3.32767]", - "System.Threading": "(,4.3.32767]", - "System.Threading.AccessControl": "(,10.0.32767]", - "System.Threading.Channels": "(,10.0.32767]", - "System.Threading.Overlapped": "(,4.3.32767]", - "System.Threading.Tasks": "(,4.3.32767]", - "System.Threading.Tasks.Dataflow": "(,10.0.32767]", - "System.Threading.Tasks.Extensions": "(,5.0.32767]", - "System.Threading.Tasks.Parallel": "(,4.3.32767]", - "System.Threading.Thread": "(,4.3.32767]", - "System.Threading.ThreadPool": "(,4.3.32767]", - "System.Threading.Timer": "(,4.3.32767]", - "System.ValueTuple": "(,4.5.32767]", - "System.Xml.ReaderWriter": "(,4.3.32767]", - "System.Xml.XDocument": "(,4.3.32767]", - "System.Xml.XmlDocument": "(,4.3.32767]", - "System.Xml.XmlSerializer": "(,4.3.32767]", - "System.Xml.XPath": "(,4.3.32767]", - "System.Xml.XPath.XDocument": "(,5.0.32767]" - } - } - } - } -} \ No newline at end of file diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/GenerationLoop.cs b/SNP_Evolution/Search/Algorithms/GenerationLoop.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/GenerationLoop.cs rename to SNP_Evolution/Search/Algorithms/GenerationLoop.cs index 92eae66..d2cf218 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/GenerationLoop.cs +++ b/SNP_Evolution/Search/Algorithms/GenerationLoop.cs @@ -1,8 +1,8 @@ using System; using System.Threading; -using SnpEvolution.Evolution.Accounting; +using SnpEvolution.Specs.Accounting; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // The budget is looked at before each generation, so one started is always finished. public static class GenerationLoop diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/GeneticAlgorithm.cs b/SNP_Evolution/Search/Algorithms/GeneticAlgorithm.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/GeneticAlgorithm.cs rename to SNP_Evolution/Search/Algorithms/GeneticAlgorithm.cs index 290be6d..d73c7f7 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/GeneticAlgorithm.cs +++ b/SNP_Evolution/Search/Algorithms/GeneticAlgorithm.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // A generational algorithm: rank everyone, carry the elite over, and breed the rest with the given operators. public sealed class GeneticAlgorithm : IGeneticAlgorithm diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/IGeneticAlgorithm.cs b/SNP_Evolution/Search/Algorithms/IGeneticAlgorithm.cs similarity index 90% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/IGeneticAlgorithm.cs rename to SNP_Evolution/Search/Algorithms/IGeneticAlgorithm.cs index e201d65..19d1338 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/IGeneticAlgorithm.cs +++ b/SNP_Evolution/Search/Algorithms/IGeneticAlgorithm.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // What the console needs from an evolution strategy, so a different algorithm can be dropped in. public interface IGeneticAlgorithm diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/Immigrants.cs b/SNP_Evolution/Search/Algorithms/Immigrants.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/Immigrants.cs rename to SNP_Evolution/Search/Algorithms/Immigrants.cs index b24d9e3..228e4f9 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/Immigrants.cs +++ b/SNP_Evolution/Search/Algorithms/Immigrants.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // Newcomers are held until the next generation, so a stall's reaction never disturbs one being scored. public sealed class Immigrants diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/IterativeEvolution.cs b/SNP_Evolution/Search/Algorithms/IterativeEvolution.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/IterativeEvolution.cs rename to SNP_Evolution/Search/Algorithms/IterativeEvolution.cs index 435b710..ceb224a 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/IterativeEvolution.cs +++ b/SNP_Evolution/Search/Algorithms/IterativeEvolution.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // A stage that has finished, or the one under way when the run stopped. public sealed record StageReport(int Length, int Generations, bool Solved, float BestFitness); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/MapElites.cs b/SNP_Evolution/Search/Algorithms/MapElites.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/MapElites.cs rename to SNP_Evolution/Search/Algorithms/MapElites.cs index 992c48b..59b45c0 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/MapElites.cs +++ b/SNP_Evolution/Search/Algorithms/MapElites.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // MAP-Elites: an archive keeps the best network for every cell, and children of random elites compete only with // the elite of their own cell. When the task describes behaviour (how much of a sequence a network gets right and diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/MuPlusLambdaStrategy.cs b/SNP_Evolution/Search/Algorithms/MuPlusLambdaStrategy.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/MuPlusLambdaStrategy.cs rename to SNP_Evolution/Search/Algorithms/MuPlusLambdaStrategy.cs index e3cfa18..a48de73 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/MuPlusLambdaStrategy.cs +++ b/SNP_Evolution/Search/Algorithms/MuPlusLambdaStrategy.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // A (mu + lambda) evolution strategy: each generation every child is a mutated copy of a random parent, and the mu // best of parents and children survive. Children win ties, so the search can drift across equally fit networks. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/Ranking.cs b/SNP_Evolution/Search/Algorithms/Ranking.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/Ranking.cs rename to SNP_Evolution/Search/Algorithms/Ranking.cs index a652400..eb4bd96 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/Ranking.cs +++ b/SNP_Evolution/Search/Algorithms/Ranking.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // Fitter first and, between equally fit candidates, smaller first: parsimony pressure that steers the search // towards the small systems the field values without ever trading away fitness. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/ScoreHistory.cs b/SNP_Evolution/Search/Algorithms/ScoreHistory.cs similarity index 88% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/ScoreHistory.cs rename to SNP_Evolution/Search/Algorithms/ScoreHistory.cs index 8da8bec..a1c0af9 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/ScoreHistory.cs +++ b/SNP_Evolution/Search/Algorithms/ScoreHistory.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // One row per scored generation, holding every in-range fitness it scored, for the fitness chart a run saves. public sealed class ScoreHistory diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/SolveCheck.cs b/SNP_Evolution/Search/Algorithms/SolveCheck.cs similarity index 88% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/SolveCheck.cs rename to SNP_Evolution/Search/Algorithms/SolveCheck.cs index f186b77..ddbd422 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/SolveCheck.cs +++ b/SNP_Evolution/Search/Algorithms/SolveCheck.cs @@ -1,8 +1,8 @@ using System; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // A failed check's score becomes the candidate's, since an elite kept with a lucky score is never rescored. public static class SolveCheck diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/SpeciatedAlgorithm.cs b/SNP_Evolution/Search/Algorithms/SpeciatedAlgorithm.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/SpeciatedAlgorithm.cs rename to SNP_Evolution/Search/Algorithms/SpeciatedAlgorithm.cs index 540d6cb..418b58e 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/SpeciatedAlgorithm.cs +++ b/SNP_Evolution/Search/Algorithms/SpeciatedAlgorithm.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // NEAT-style evolution. Networks are grouped into species of similar structure, each network's fitness is shared // with its species, and species breed among themselves. A new structure therefore only competes with its own diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/StagnationRecovery.cs b/SNP_Evolution/Search/Algorithms/StagnationRecovery.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/StagnationRecovery.cs rename to SNP_Evolution/Search/Algorithms/StagnationRecovery.cs index d2af950..6ae10dd 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/StagnationRecovery.cs +++ b/SNP_Evolution/Search/Algorithms/StagnationRecovery.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { // When to react to a stalled search, and how hard. Patience is the generations without improvement before each // reaction; ImmigrantFraction is the share of the population replaced when escalating; after MaxExtraEdits diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/StallDetector.cs b/SNP_Evolution/Search/Algorithms/StallDetector.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Algorithms/StallDetector.cs rename to SNP_Evolution/Search/Algorithms/StallDetector.cs index 10d381b..cf66df5 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Algorithms/StallDetector.cs +++ b/SNP_Evolution/Search/Algorithms/StallDetector.cs @@ -1,4 +1,4 @@ -namespace SnpEvolution.Evolution.Algorithms +namespace SnpEvolution.Search.Algorithms { public enum Progress { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/AlgorithmSelector.cs b/SNP_Evolution/Search/Benchmarking/AlgorithmSelector.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/AlgorithmSelector.cs rename to SNP_Evolution/Search/Benchmarking/AlgorithmSelector.cs index f55574d..0f6c34a 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/AlgorithmSelector.cs +++ b/SNP_Evolution/Search/Benchmarking/AlgorithmSelector.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Search; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Evolution.Benchmarking +namespace SnpEvolution.Search.Benchmarking { public sealed record SelectionRound(long Budget, IReadOnlyList Standings, IReadOnlyList Advancing); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/Benchmark.cs b/SNP_Evolution/Search/Benchmarking/Benchmark.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/Benchmark.cs rename to SNP_Evolution/Search/Benchmarking/Benchmark.cs index ed52ea0..e2b0caa 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/Benchmark.cs +++ b/SNP_Evolution/Search/Benchmarking/Benchmark.cs @@ -5,19 +5,19 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; -namespace SnpEvolution.Evolution.Benchmarking +namespace SnpEvolution.Search.Benchmarking { // How every benchmark run is set up. Budgets are counted in network evaluations rather than time, so results // compare fairly across machines and engines. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/TaskSuite.cs b/SNP_Evolution/Search/Benchmarking/TaskSuite.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/TaskSuite.cs rename to SNP_Evolution/Search/Benchmarking/TaskSuite.cs index 144f7c6..1e784f5 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Benchmarking/TaskSuite.cs +++ b/SNP_Evolution/Search/Benchmarking/TaskSuite.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search.Genome; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Benchmarking +namespace SnpEvolution.Search.Benchmarking { // A task together with how to search for it: which rule form to build networks from and how outputs are timed. public sealed record BenchmarkTask(ITask Task, RuleForm RuleForm, OutputTiming Timing) diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/CompositionSpace.cs b/SNP_Evolution/Search/CompositionSpace.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/CompositionSpace.cs rename to SNP_Evolution/Search/CompositionSpace.cs index e673941..20cc284 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/CompositionSpace.cs +++ b/SNP_Evolution/Search/CompositionSpace.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // Part bodies are left out of the glue cap, since they are verified already and the cap is there to bound the search. public sealed class CompositionSpace diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/EvolutionContext.cs b/SNP_Evolution/Search/EvolutionContext.cs similarity index 84% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/EvolutionContext.cs rename to SNP_Evolution/Search/EvolutionContext.cs index 889c838..c9d4f1e 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/EvolutionContext.cs +++ b/SNP_Evolution/Search/EvolutionContext.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Parts; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // Everything an algorithm needs to start evolving, independent of which algorithm it is. Lexicase makes parents // be picked by lexicase selection where the algorithm picks parents; Modules lets mutation use a module library. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/EvolutionSearch.cs b/SNP_Evolution/Search/EvolutionSearch.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/EvolutionSearch.cs rename to SNP_Evolution/Search/EvolutionSearch.cs index 1dbe847..5c964a0 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/EvolutionSearch.cs +++ b/SNP_Evolution/Search/EvolutionSearch.cs @@ -1,11 +1,11 @@ using System; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Accounting; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // Seeds become the starting networks, and a stall policy wraps the algorithm in stagnation recovery. public class EvolutionSearch : ISearch diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Fitness/CheckDiagnosis.cs b/SNP_Evolution/Search/Fitness/CheckDiagnosis.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Fitness/CheckDiagnosis.cs rename to SNP_Evolution/Search/Fitness/CheckDiagnosis.cs index 7eb77fe..5ca64d3 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Fitness/CheckDiagnosis.cs +++ b/SNP_Evolution/Search/Fitness/CheckDiagnosis.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Fitness +namespace SnpEvolution.Search.Fitness { // What a population can and cannot do yet, read from the task's checks: which checks some network gets right, // which none does, and the first of those, the frontier, which is what a stalled run is missing. Following diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Fitness/FitnessEvaluator.cs b/SNP_Evolution/Search/Fitness/FitnessEvaluator.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Fitness/FitnessEvaluator.cs rename to SNP_Evolution/Search/Fitness/FitnessEvaluator.cs index d5c0bb8..008a94f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Fitness/FitnessEvaluator.cs +++ b/SNP_Evolution/Search/Fitness/FitnessEvaluator.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Fitness +namespace SnpEvolution.Search.Fitness { // Outputs are the first case's, which for a generator is everything it produced. Exact is true when every case // was followed exhaustively, so the fitness is certain rather than sampled. Niche is the task's behaviour cell, and diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Fitness/Individual.cs b/SNP_Evolution/Search/Fitness/Individual.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Fitness/Individual.cs rename to SNP_Evolution/Search/Fitness/Individual.cs index 8649151..484c115 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Fitness/Individual.cs +++ b/SNP_Evolution/Search/Fitness/Individual.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Fitness +namespace SnpEvolution.Search.Fitness { // A candidate with a score: what ranking and lexicase selection compare, whether it is a network or a program. // Size breaks ties between equally fit candidates, smaller first. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Genome/ExpressionGenerator.cs b/SNP_Evolution/Search/Genome/ExpressionGenerator.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Genome/ExpressionGenerator.cs rename to SNP_Evolution/Search/Genome/ExpressionGenerator.cs index 36f65b3..ff63dcf 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Genome/ExpressionGenerator.cs +++ b/SNP_Evolution/Search/Genome/ExpressionGenerator.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace SnpEvolution.Evolution.Genome +namespace SnpEvolution.Search.Genome { public sealed class ExpressionGenerator { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Genome/NetworkFactory.cs b/SNP_Evolution/Search/Genome/NetworkFactory.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Genome/NetworkFactory.cs rename to SNP_Evolution/Search/Genome/NetworkFactory.cs index 3bf1624..06e9312 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Genome/NetworkFactory.cs +++ b/SNP_Evolution/Search/Genome/NetworkFactory.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Genome +namespace SnpEvolution.Search.Genome { // Which rule form random rules take. public enum RuleForm diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Genome/RandomTopology.cs b/SNP_Evolution/Search/Genome/RandomTopology.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Genome/RandomTopology.cs rename to SNP_Evolution/Search/Genome/RandomTopology.cs index 598efeb..6760ebd 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Genome/RandomTopology.cs +++ b/SNP_Evolution/Search/Genome/RandomTopology.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Genome +namespace SnpEvolution.Search.Genome { public static class RandomTopology { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/ISearch.cs b/SNP_Evolution/Search/ISearch.cs similarity index 88% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/ISearch.cs rename to SNP_Evolution/Search/ISearch.cs index 1c072e0..001e897 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/ISearch.cs +++ b/SNP_Evolution/Search/ISearch.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Threading; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { public interface ISearch { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Modules/CompositionEdits.cs b/SNP_Evolution/Search/Modules/CompositionEdits.cs similarity index 91% rename from SNP_Evolution/SNP_Evolution/Evolution/Modules/CompositionEdits.cs rename to SNP_Evolution/Search/Modules/CompositionEdits.cs index be69f6a..bb634a1 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Modules/CompositionEdits.cs +++ b/SNP_Evolution/Search/Modules/CompositionEdits.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Parts; -namespace SnpEvolution.Evolution.Modules +namespace SnpEvolution.Search.Modules { // Edit weights are relative to each other, and MaxGlue 0 means the genome space's neuron cap. public sealed record CompositionMix( diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Modules/HandBuiltMachines.cs b/SNP_Evolution/Search/Modules/HandBuiltMachines.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Modules/HandBuiltMachines.cs rename to SNP_Evolution/Search/Modules/HandBuiltMachines.cs index 4aa96fa..57a3c3f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Modules/HandBuiltMachines.cs +++ b/SNP_Evolution/Search/Modules/HandBuiltMachines.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; -namespace SnpEvolution.Evolution.Modules +namespace SnpEvolution.Search.Modules { // Opt-in only, since the default library is one the runs discover themselves. public static class HandBuiltMachines diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Modules/ModularEvolution.cs b/SNP_Evolution/Search/Modules/ModularEvolution.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Modules/ModularEvolution.cs rename to SNP_Evolution/Search/Modules/ModularEvolution.cs index b698140..be669ea 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Modules/ModularEvolution.cs +++ b/SNP_Evolution/Search/Modules/ModularEvolution.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Modules +namespace SnpEvolution.Search.Modules { // How the modular loop reacts: Patience is the generations without improvement before it does, SideGenerations // the most a side run gets, and CompositeFraction the share of the population that gets a new module copy. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleMutations.cs b/SNP_Evolution/Search/Modules/ModuleMutations.cs similarity index 91% rename from SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleMutations.cs rename to SNP_Evolution/Search/Modules/ModuleMutations.cs index ae917ed..24d9765 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleMutations.cs +++ b/SNP_Evolution/Search/Modules/ModuleMutations.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; -namespace SnpEvolution.Evolution.Modules +namespace SnpEvolution.Search.Modules { // Puts a copy of a module from the library into the network. public sealed class InsertModule : IMutation diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleSupport.cs b/SNP_Evolution/Search/Modules/ModuleSupport.cs similarity index 82% rename from SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleSupport.cs rename to SNP_Evolution/Search/Modules/ModuleSupport.cs index d9fdd1a..94d11a1 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleSupport.cs +++ b/SNP_Evolution/Search/Modules/ModuleSupport.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Parts; +using SnpEvolution.Specs.Parts; -namespace SnpEvolution.Evolution.Modules +namespace SnpEvolution.Search.Modules { // The library and how modules are treated, for building the mutation. Tracker is set for the main run only, so // credit and harvesting follow the task the run is scored on. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleTracker.cs b/SNP_Evolution/Search/Modules/ModuleTracker.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleTracker.cs rename to SNP_Evolution/Search/Modules/ModuleTracker.cs index 247cf66..9da24dd 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Modules/ModuleTracker.cs +++ b/SNP_Evolution/Search/Modules/ModuleTracker.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Modules +namespace SnpEvolution.Search.Modules { // Remembers which network each child was made from and what every network scored, so that when a child is // scored it can credit the modules newly put into it, and when a child sets a new best and gets a check right that diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Modules/PartMutations.cs b/SNP_Evolution/Search/Modules/PartMutations.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Modules/PartMutations.cs rename to SNP_Evolution/Search/Modules/PartMutations.cs index e31179f..15d8aee 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Modules/PartMutations.cs +++ b/SNP_Evolution/Search/Modules/PartMutations.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; -namespace SnpEvolution.Evolution.Modules +namespace SnpEvolution.Search.Modules { // Moves one end of a port to a fitting port of another copy or to untyped glue; input neurons stay as they are, since only the environment feeds them. public sealed class RewirePort : IMutation diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Modules/Promotion.cs b/SNP_Evolution/Search/Modules/Promotion.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution/Evolution/Modules/Promotion.cs rename to SNP_Evolution/Search/Modules/Promotion.cs index 2598f79..75742a2 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Modules/Promotion.cs +++ b/SNP_Evolution/Search/Modules/Promotion.cs @@ -2,14 +2,14 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; -namespace SnpEvolution.Evolution.Modules +namespace SnpEvolution.Search.Modules { // What promoting a composition came to: a passing verdict with the module it became, or the verdict that kept it out. public sealed record Promoted(Verdict Verdict, Module? Module = null); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/NetworkSetup.cs b/SNP_Evolution/Search/NetworkSetup.cs similarity index 76% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/NetworkSetup.cs rename to SNP_Evolution/Search/NetworkSetup.cs index 3a72148..64915d2 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/NetworkSetup.cs +++ b/SNP_Evolution/Search/NetworkSetup.cs @@ -1,15 +1,15 @@ using System; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { public sealed record NetworkScoring(Func CreateEngine, SimulationOptions Options, int SolvedRetests) { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/GeneticOperators.cs b/SNP_Evolution/Search/Operators/GeneticOperators.cs similarity index 87% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/GeneticOperators.cs rename to SNP_Evolution/Search/Operators/GeneticOperators.cs index 1f84e3b..feaaa16 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/GeneticOperators.cs +++ b/SNP_Evolution/Search/Operators/GeneticOperators.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { public sealed record GeneticOperators(IParentSelection Selection, ICrossover Crossover, IMutation Mutation); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/LexicaseSelection.cs b/SNP_Evolution/Search/Operators/LexicaseSelection.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/LexicaseSelection.cs rename to SNP_Evolution/Search/Operators/LexicaseSelection.cs index 8b731b4..23dc4eb 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/LexicaseSelection.cs +++ b/SNP_Evolution/Search/Operators/LexicaseSelection.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Fitness; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Accounting; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // Lexicase selection (Helmuth, Spector and Matheson 2015): each pick goes through the task's checks in a fresh // random order, keeping only the networks that do best on each, until one is left. A network that is the only diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/MutationPressure.cs b/SNP_Evolution/Search/Operators/MutationPressure.cs similarity index 90% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/MutationPressure.cs rename to SNP_Evolution/Search/Operators/MutationPressure.cs index 5e25211..2dd3a20 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/MutationPressure.cs +++ b/SNP_Evolution/Search/Operators/MutationPressure.cs @@ -1,4 +1,4 @@ -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // How hard mutation pushes, shared between an algorithm's mutation and whatever watches its progress. At zero // mutation behaves as configured; each extra edit makes every child at least that many more edits away from its diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/NeuronCrossover.cs b/SNP_Evolution/Search/Operators/NeuronCrossover.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/NeuronCrossover.cs rename to SNP_Evolution/Search/Operators/NeuronCrossover.cs index e2ecebf..c1ba87b 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/NeuronCrossover.cs +++ b/SNP_Evolution/Search/Operators/NeuronCrossover.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // The child has the first parent's neurons and roles, and takes each neuron's rules, spikes and synapses from // either parent at the same position, so parents with different topologies can still be crossed. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/ProfileOperators.cs b/SNP_Evolution/Search/Operators/ProfileOperators.cs similarity index 88% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/ProfileOperators.cs rename to SNP_Evolution/Search/Operators/ProfileOperators.cs index c955ff5..a8a1e0f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/ProfileOperators.cs +++ b/SNP_Evolution/Search/Operators/ProfileOperators.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // Puts what another operator makes back within the profile, so every child of a profile run fits it. public sealed class ProfileMutation : IMutation diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/RouletteWheelSelection.cs b/SNP_Evolution/Search/Operators/RouletteWheelSelection.cs similarity index 92% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/RouletteWheelSelection.cs rename to SNP_Evolution/Search/Operators/RouletteWheelSelection.cs index f0d459b..6416bbf 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/RouletteWheelSelection.cs +++ b/SNP_Evolution/Search/Operators/RouletteWheelSelection.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // Picks parents in proportion to fitness, falling back to the top tenth when rounding leaves the wheel unspent. public sealed class RouletteWheelSelection : IParentSelection diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/RuleExpressionCrossover.cs b/SNP_Evolution/Search/Operators/RuleExpressionCrossover.cs similarity index 93% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/RuleExpressionCrossover.cs rename to SNP_Evolution/Search/Operators/RuleExpressionCrossover.cs index 3a88b4c..c0a13d0 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/RuleExpressionCrossover.cs +++ b/SNP_Evolution/Search/Operators/RuleExpressionCrossover.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // The child keeps the first parent's topology, taking each rule expression from either parent. public sealed class RuleExpressionCrossover : ICrossover diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/RuleExpressionMutation.cs b/SNP_Evolution/Search/Operators/RuleExpressionMutation.cs similarity index 93% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/RuleExpressionMutation.cs rename to SNP_Evolution/Search/Operators/RuleExpressionMutation.cs index 61bc323..49e55e0 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/RuleExpressionMutation.cs +++ b/SNP_Evolution/Search/Operators/RuleExpressionMutation.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // With probability rate, replaces one randomly chosen rule's expression with a freshly generated one. public sealed class RuleExpressionMutation : IMutation diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/ShrinkEdits.cs b/SNP_Evolution/Search/Operators/ShrinkEdits.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/ShrinkEdits.cs rename to SNP_Evolution/Search/Operators/ShrinkEdits.cs index 2d38a8f..5d907f9 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/ShrinkEdits.cs +++ b/SNP_Evolution/Search/Operators/ShrinkEdits.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // Removes a hidden neuron but keeps the path through it: every neuron that sent to it sends to its targets // instead. A relay that only passes spikes on costs a step and a neuron, and the hand reductions of SN P systems diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/StructuralMutations.cs b/SNP_Evolution/Search/Operators/StructuralMutations.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/StructuralMutations.cs rename to SNP_Evolution/Search/Operators/StructuralMutations.cs index 6112825..9cf8319 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/StructuralMutations.cs +++ b/SNP_Evolution/Search/Operators/StructuralMutations.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // The edits below always change the network when they can, and return it unchanged when they cannot (removing // the only rule, say). WeightedMutation decides how often to mutate and which edit to make. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/TournamentSelection.cs b/SNP_Evolution/Search/Operators/TournamentSelection.cs similarity index 89% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/TournamentSelection.cs rename to SNP_Evolution/Search/Operators/TournamentSelection.cs index 31c3e63..1cc16f4 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/TournamentSelection.cs +++ b/SNP_Evolution/Search/Operators/TournamentSelection.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Fitness; +using SnpEvolution.Search.Fitness; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { // Draws a few individuals at random and keeps the fittest, which stays selective even when fitnesses are close. public sealed class TournamentSelection : IParentSelection diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Operators/WeightedEdit.cs b/SNP_Evolution/Search/Operators/WeightedEdit.cs similarity index 78% rename from SNP_Evolution/SNP_Evolution/Evolution/Operators/WeightedEdit.cs rename to SNP_Evolution/Search/Operators/WeightedEdit.cs index 4bc233a..d846036 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Operators/WeightedEdit.cs +++ b/SNP_Evolution/Search/Operators/WeightedEdit.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Operators +namespace SnpEvolution.Search.Operators { public sealed record WeightedEdit(string Name, IMutation Edit, double Weight); } diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/PartSearch.cs b/SNP_Evolution/Search/PartSearch.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/PartSearch.cs rename to SNP_Evolution/Search/PartSearch.cs index e62f7eb..80f5f92 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/PartSearch.cs +++ b/SNP_Evolution/Search/PartSearch.cs @@ -1,18 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // RobustJitter above 0 makes the shrink keep the most robust part that verifies rather than the cheapest. public sealed record PartSearchSettings( diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramEdits.cs b/SNP_Evolution/Search/ProgramEdits.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramEdits.cs rename to SNP_Evolution/Search/ProgramEdits.cs index ae7f6ff..c4afd2e 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramEdits.cs +++ b/SNP_Evolution/Search/ProgramEdits.cs @@ -3,7 +3,7 @@ using System.Linq; using SnpEvolution.Compilation; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { public sealed class ProgramEdits { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramScoring.cs b/SNP_Evolution/Search/ProgramScoring.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramScoring.cs rename to SNP_Evolution/Search/ProgramScoring.cs index 068d6d7..862408c 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramScoring.cs +++ b/SNP_Evolution/Search/ProgramScoring.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Compilation; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // The last check is the share of generated numbers in the target, so lexicase rewards generating nothing extra. public sealed record ScoredProgram(RegisterProgram Program, float Fitness, IReadOnlyList Outputs, IReadOnlyList Checks) : IScored diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramSearch.cs b/SNP_Evolution/Search/ProgramSearch.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramSearch.cs rename to SNP_Evolution/Search/ProgramSearch.cs index 93c9a2d..06ebae7 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/ProgramSearch.cs +++ b/SNP_Evolution/Search/ProgramSearch.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Compilation; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { public sealed record ProgramSearchSettings(int Population = 100, int MaxInstructions = 16, int Registers = 4, int MaxConfigurations = 2_000, bool Lexicase = true); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Proposals/PartProposals.cs b/SNP_Evolution/Search/Proposals/PartProposals.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Proposals/PartProposals.cs rename to SNP_Evolution/Search/Proposals/PartProposals.cs index ff5f64e..e6fcc6f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Proposals/PartProposals.cs +++ b/SNP_Evolution/Search/Proposals/PartProposals.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Search; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; - -namespace SnpEvolution.Evolution.Proposals +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; + +namespace SnpEvolution.Search.Proposals { // MaxProposals is low because each proposal costs a whole part evolution. public sealed record ProposalPolicy(int Patience = 25, int MaxProposals = 6); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Proposals/RecurrenceProposer.cs b/SNP_Evolution/Search/Proposals/RecurrenceProposer.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Proposals/RecurrenceProposer.cs rename to SNP_Evolution/Search/Proposals/RecurrenceProposer.cs index 2cb4f88..8c9cc4f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Proposals/RecurrenceProposer.cs +++ b/SNP_Evolution/Search/Proposals/RecurrenceProposer.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Linq; using SnpEvolution.Compilation; -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Proposals +namespace SnpEvolution.Search.Proposals { public sealed record ShapeProposal(string Form, IReadOnlyList Contracts) { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/RandomComposition.cs b/SNP_Evolution/Search/RandomComposition.cs similarity index 91% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/RandomComposition.cs rename to SNP_Evolution/Search/RandomComposition.cs index a5e1c17..4dbea94 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/RandomComposition.cs +++ b/SNP_Evolution/Search/RandomComposition.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // A random composition for a search to start from: random glue and library parts wired in by port type. public static class RandomComposition diff --git a/SNP_Evolution/Search/Search.csproj b/SNP_Evolution/Search/Search.csproj new file mode 100644 index 0000000..e660eb3 --- /dev/null +++ b/SNP_Evolution/Search/Search.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/SearchCatalog.cs b/SNP_Evolution/Search/SearchCatalog.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/SearchCatalog.cs rename to SNP_Evolution/Search/SearchCatalog.cs index f9e1a5c..d5a46d2 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/SearchCatalog.cs +++ b/SNP_Evolution/Search/SearchCatalog.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // Registering here is all a new search needs, since callers pick searches by type rather than by name. public static class SearchCatalog diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/ShrinkSearch.cs b/SNP_Evolution/Search/ShrinkSearch.cs similarity index 93% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/ShrinkSearch.cs rename to SNP_Evolution/Search/ShrinkSearch.cs index b2ca6ac..4fcde72 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/ShrinkSearch.cs +++ b/SNP_Evolution/Search/ShrinkSearch.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Algorithms; -using SnpEvolution.Evolution.Fitness; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Operators; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; +using SnpEvolution.Specs.Verification; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // After STOKE (Schkufza, Sharma and Aiken 2013): the seed is taken to solve the task, and only a cheaper network that passes the check replaces it. public sealed class ShrinkSearch : ISearch diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Search/WeightedMutation.cs b/SNP_Evolution/Search/WeightedMutation.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Search/WeightedMutation.cs rename to SNP_Evolution/Search/WeightedMutation.cs index 39573b5..f1139e7 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Search/WeightedMutation.cs +++ b/SNP_Evolution/Search/WeightedMutation.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Genome; -using SnpEvolution.Evolution.Modules; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Search.Genome; +using SnpEvolution.Search.Modules; +using SnpEvolution.Search.Operators; -namespace SnpEvolution.Evolution.Search +namespace SnpEvolution.Search { // With probability rate, makes one edit chosen by weight, and keeps making more with probability repeatChance, // so most children are one small step away while a few take a bigger leap. Under pressure every child is mutated diff --git a/SNP_Evolution/SNP_Evolution/Simulation/AxonBuffer.cs b/SNP_Evolution/Simulation/AxonBuffer.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/AxonBuffer.cs rename to SNP_Evolution/Simulation/AxonBuffer.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/CompiledNetwork.cs b/SNP_Evolution/Simulation/CompiledNetwork.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Simulation/CompiledNetwork.cs rename to SNP_Evolution/Simulation/CompiledNetwork.cs index ec0b656..fe7c0f7 100644 --- a/SNP_Evolution/SNP_Evolution/Simulation/CompiledNetwork.cs +++ b/SNP_Evolution/Simulation/CompiledNetwork.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Simulation { diff --git a/SNP_Evolution/SNP_Evolution/Simulation/EngineSupport.cs b/SNP_Evolution/Simulation/EngineSupport.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/EngineSupport.cs rename to SNP_Evolution/Simulation/EngineSupport.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/ExhaustiveCpuEngine.cs b/SNP_Evolution/Simulation/ExhaustiveCpuEngine.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/ExhaustiveCpuEngine.cs rename to SNP_Evolution/Simulation/ExhaustiveCpuEngine.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/ISimulationEngine.cs b/SNP_Evolution/Simulation/ISimulationEngine.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Simulation/ISimulationEngine.cs rename to SNP_Evolution/Simulation/ISimulationEngine.cs index 7669768..2f3f05d 100644 --- a/SNP_Evolution/SNP_Evolution/Simulation/ISimulationEngine.cs +++ b/SNP_Evolution/Simulation/ISimulationEngine.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Simulation { diff --git a/SNP_Evolution/SNP_Evolution/Simulation/InputSpikes.cs b/SNP_Evolution/Simulation/InputSpikes.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/InputSpikes.cs rename to SNP_Evolution/Simulation/InputSpikes.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/JitterBuffer.cs b/SNP_Evolution/Simulation/JitterBuffer.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/JitterBuffer.cs rename to SNP_Evolution/Simulation/JitterBuffer.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Metal/GpuBatch.cs b/SNP_Evolution/Simulation/Metal/GpuBatch.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Simulation/Metal/GpuBatch.cs rename to SNP_Evolution/Simulation/Metal/GpuBatch.cs index 6a29d82..52d34ed 100644 --- a/SNP_Evolution/SNP_Evolution/Simulation/Metal/GpuBatch.cs +++ b/SNP_Evolution/Simulation/Metal/GpuBatch.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Simulation.Metal { diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Metal/GpuNetwork.cs b/SNP_Evolution/Simulation/Metal/GpuNetwork.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/Metal/GpuNetwork.cs rename to SNP_Evolution/Simulation/Metal/GpuNetwork.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Metal/MetalBuffers.cs b/SNP_Evolution/Simulation/Metal/MetalBuffers.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/Metal/MetalBuffers.cs rename to SNP_Evolution/Simulation/Metal/MetalBuffers.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Metal/MetalContext.cs b/SNP_Evolution/Simulation/Metal/MetalContext.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/Metal/MetalContext.cs rename to SNP_Evolution/Simulation/Metal/MetalContext.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Metal/MetalEngine.cs b/SNP_Evolution/Simulation/Metal/MetalEngine.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/Metal/MetalEngine.cs rename to SNP_Evolution/Simulation/Metal/MetalEngine.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Metal/ObjectiveC.cs b/SNP_Evolution/Simulation/Metal/ObjectiveC.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/Metal/ObjectiveC.cs rename to SNP_Evolution/Simulation/Metal/ObjectiveC.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Metal/Simulation.metal b/SNP_Evolution/Simulation/Metal/Simulation.metal similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/Metal/Simulation.metal rename to SNP_Evolution/Simulation/Metal/Simulation.metal diff --git a/SNP_Evolution/SNP_Evolution/Simulation/NetworkRunner.cs b/SNP_Evolution/Simulation/NetworkRunner.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Simulation/NetworkRunner.cs rename to SNP_Evolution/Simulation/NetworkRunner.cs index c26ecd9..0f32633 100644 --- a/SNP_Evolution/SNP_Evolution/Simulation/NetworkRunner.cs +++ b/SNP_Evolution/Simulation/NetworkRunner.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Simulation { diff --git a/SNP_Evolution/SNP_Evolution/Simulation/NetworkSimulation.cs b/SNP_Evolution/Simulation/NetworkSimulation.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Simulation/NetworkSimulation.cs rename to SNP_Evolution/Simulation/NetworkSimulation.cs index 8852e1d..c92b737 100644 --- a/SNP_Evolution/SNP_Evolution/Simulation/NetworkSimulation.cs +++ b/SNP_Evolution/Simulation/NetworkSimulation.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Simulation { diff --git a/SNP_Evolution/SNP_Evolution/Simulation/NetworkStep.cs b/SNP_Evolution/Simulation/NetworkStep.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Simulation/NetworkStep.cs rename to SNP_Evolution/Simulation/NetworkStep.cs index 62e62a5..a5cd715 100644 --- a/SNP_Evolution/SNP_Evolution/Simulation/NetworkStep.cs +++ b/SNP_Evolution/Simulation/NetworkStep.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Simulation { diff --git a/SNP_Evolution/SNP_Evolution/Simulation/NeuronStates.cs b/SNP_Evolution/Simulation/NeuronStates.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/NeuronStates.cs rename to SNP_Evolution/Simulation/NeuronStates.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/OutputDecoder.cs b/SNP_Evolution/Simulation/OutputDecoder.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/OutputDecoder.cs rename to SNP_Evolution/Simulation/OutputDecoder.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/OutputTiming.cs b/SNP_Evolution/Simulation/OutputTiming.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/OutputTiming.cs rename to SNP_Evolution/Simulation/OutputTiming.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/ParallelCpuEngine.cs b/SNP_Evolution/Simulation/ParallelCpuEngine.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/ParallelCpuEngine.cs rename to SNP_Evolution/Simulation/ParallelCpuEngine.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/PortRecorder.cs b/SNP_Evolution/Simulation/PortRecorder.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/PortRecorder.cs rename to SNP_Evolution/Simulation/PortRecorder.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/RoutedEngine.cs b/SNP_Evolution/Simulation/RoutedEngine.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/RoutedEngine.cs rename to SNP_Evolution/Simulation/RoutedEngine.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Sampling.cs b/SNP_Evolution/Simulation/Sampling.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/Sampling.cs rename to SNP_Evolution/Simulation/Sampling.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/SequentialCpuEngine.cs b/SNP_Evolution/Simulation/SequentialCpuEngine.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/SequentialCpuEngine.cs rename to SNP_Evolution/Simulation/SequentialCpuEngine.cs diff --git a/SNP_Evolution/Simulation/Simulation.csproj b/SNP_Evolution/Simulation/Simulation.csproj new file mode 100644 index 0000000..302c858 --- /dev/null +++ b/SNP_Evolution/Simulation/Simulation.csproj @@ -0,0 +1,15 @@ + + + + true + + + + + + + + + + + diff --git a/SNP_Evolution/SNP_Evolution/Simulation/SimulationOptions.cs b/SNP_Evolution/Simulation/SimulationOptions.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/SimulationOptions.cs rename to SNP_Evolution/Simulation/SimulationOptions.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/SpikeRelease.cs b/SNP_Evolution/Simulation/SpikeRelease.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/SpikeRelease.cs rename to SNP_Evolution/Simulation/SpikeRelease.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/StateKey.cs b/SNP_Evolution/Simulation/StateKey.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/StateKey.cs rename to SNP_Evolution/Simulation/StateKey.cs diff --git a/SNP_Evolution/SNP_Evolution/Simulation/Trial.cs b/SNP_Evolution/Simulation/Trial.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Simulation/Trial.cs rename to SNP_Evolution/Simulation/Trial.cs index 64724cf..854517b 100644 --- a/SNP_Evolution/SNP_Evolution/Simulation/Trial.cs +++ b/SNP_Evolution/Simulation/Trial.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Simulation { diff --git a/SNP_Evolution/SNP_Evolution/Simulation/TrialResult.cs b/SNP_Evolution/Simulation/TrialResult.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Simulation/TrialResult.cs rename to SNP_Evolution/Simulation/TrialResult.cs diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Accounting/EvaluationBudget.cs b/SNP_Evolution/Specs/Accounting/EvaluationBudget.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Evolution/Accounting/EvaluationBudget.cs rename to SNP_Evolution/Specs/Accounting/EvaluationBudget.cs index 24267e6..0aaa55a 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Accounting/EvaluationBudget.cs +++ b/SNP_Evolution/Specs/Accounting/EvaluationBudget.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Threading; -namespace SnpEvolution.Evolution.Accounting +namespace SnpEvolution.Specs.Accounting { // Only Network counts towards a budget's limit; the other kinds are reported beside it. public enum EvaluationKind diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Accounting/SearchOutcome.cs b/SNP_Evolution/Specs/Accounting/SearchOutcome.cs similarity index 93% rename from SNP_Evolution/SNP_Evolution/Evolution/Accounting/SearchOutcome.cs rename to SNP_Evolution/Specs/Accounting/SearchOutcome.cs index 2aeaded..5fc4270 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Accounting/SearchOutcome.cs +++ b/SNP_Evolution/Specs/Accounting/SearchOutcome.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace SnpEvolution.Evolution.Accounting +namespace SnpEvolution.Specs.Accounting { public enum SearchStop { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Accounting/Statistics.cs b/SNP_Evolution/Specs/Accounting/Statistics.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Evolution/Accounting/Statistics.cs rename to SNP_Evolution/Specs/Accounting/Statistics.cs index a8646b9..43dcb13 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Accounting/Statistics.cs +++ b/SNP_Evolution/Specs/Accounting/Statistics.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Numerics; -namespace SnpEvolution.Evolution.Accounting +namespace SnpEvolution.Specs.Accounting { // U counts the pairs the first sample wins, ties as a half, and A12 is U over all pairs; P is two-sided. public sealed record MannWhitneyResult(double U, double P, double A12, bool Exact); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/ArithmeticParts.cs b/SNP_Evolution/Specs/Contracts/ArithmeticParts.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/ArithmeticParts.cs rename to SNP_Evolution/Specs/Contracts/ArithmeticParts.cs index 7d419e6..0049352 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/ArithmeticParts.cs +++ b/SNP_Evolution/Specs/Contracts/ArithmeticParts.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using static SnpEvolution.Evolution.Contracts.Specifications; +using static SnpEvolution.Specs.Contracts.Specifications; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { // Cases include zero operands and one larger case, where hand-built SN P arithmetic usually breaks. Each contract is a // specification and the values its cases test. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/CatalogueEntry.cs b/SNP_Evolution/Specs/Contracts/CatalogueEntry.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/CatalogueEntry.cs rename to SNP_Evolution/Specs/Contracts/CatalogueEntry.cs index 1815088..6345d9b 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/CatalogueEntry.cs +++ b/SNP_Evolution/Specs/Contracts/CatalogueEntry.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { // A catalogued contract: the specification it is built from and the values its cases test, each row a value per // data in-port in order. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/Contract.cs b/SNP_Evolution/Specs/Contracts/Contract.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/Contract.cs rename to SNP_Evolution/Specs/Contracts/Contract.cs index 15ab498..4b3f371 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/Contract.cs +++ b/SNP_Evolution/Specs/Contracts/Contract.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { // How a value travels along a port, as spikes on the port's neuron. [JsonConverter(typeof(StringEnumConverter))] diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/FirstParts.cs b/SNP_Evolution/Specs/Contracts/FirstParts.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/FirstParts.cs rename to SNP_Evolution/Specs/Contracts/FirstParts.cs index 7d82353..b28c897 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/FirstParts.cs +++ b/SNP_Evolution/Specs/Contracts/FirstParts.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using static SnpEvolution.Evolution.Contracts.Specifications; +using static SnpEvolution.Specs.Contracts.Specifications; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { // One row of the first-parts table: a kind of part, its ports and goal in words, and its contracts, one per variant // (delay k = 1..4, sequencer k = 2, 3). diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortBinding.cs b/SNP_Evolution/Specs/Contracts/PortBinding.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortBinding.cs rename to SNP_Evolution/Specs/Contracts/PortBinding.cs index 3e73298..2b88caf 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortBinding.cs +++ b/SNP_Evolution/Specs/Contracts/PortBinding.cs @@ -3,7 +3,7 @@ using System.Linq; using Newtonsoft.Json; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { // 1-based neuron positions for out-ports and done ports; in-ports feed the input neurons in contract order instead. public sealed record PortBinding(IReadOnlyDictionary Positions) diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortEncoding.cs b/SNP_Evolution/Specs/Contracts/PortEncoding.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortEncoding.cs rename to SNP_Evolution/Specs/Contracts/PortEncoding.cs index c0a21b7..321ab68 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortEncoding.cs +++ b/SNP_Evolution/Specs/Contracts/PortEncoding.cs @@ -3,7 +3,7 @@ using System.Linq; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { public sealed record EncodedCase(InputSpikes Input, int StartStep); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortLayout.cs b/SNP_Evolution/Specs/Contracts/PortLayout.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortLayout.cs rename to SNP_Evolution/Specs/Contracts/PortLayout.cs index 481c96d..efbdddd 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/PortLayout.cs +++ b/SNP_Evolution/Specs/Contracts/PortLayout.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { // A contract's port at a 1-based neuron position. public sealed record PartPort(Port Port, int Position); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/Specification.cs b/SNP_Evolution/Specs/Contracts/Specification.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/Specification.cs rename to SNP_Evolution/Specs/Contracts/Specification.cs index f1082f3..b74344b 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/Specification.cs +++ b/SNP_Evolution/Specs/Contracts/Specification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { // What a part computes for every input rather than for its test cases: its name and ports, which inputs it covers, // the case each input should give, and the latency it is allowed on that input. A contract is built from it and the diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/Specifications.cs b/SNP_Evolution/Specs/Contracts/Specifications.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Evolution/Contracts/Specifications.cs rename to SNP_Evolution/Specs/Contracts/Specifications.cs index b018d93..d2f5591 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Contracts/Specifications.cs +++ b/SNP_Evolution/Specs/Contracts/Specifications.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Contracts +namespace SnpEvolution.Specs.Contracts { // Every function a catalogued part computes, written once. FirstParts and ArithmeticParts build their contracts from // these and the inputs to test; tasks that compute the same functions read them from here too. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/Composition.cs b/SNP_Evolution/Specs/Parts/Composition.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/Composition.cs rename to SNP_Evolution/Specs/Parts/Composition.cs index fb66390..f0f7f11 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/Composition.cs +++ b/SNP_Evolution/Specs/Parts/Composition.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // A neuron of a composition: Instance 0 is glue, with Neuron its 1-based place among the glue; otherwise Neuron is // the 1-based place within that part instance, in the part's own neuron order. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/HandBuiltParts.cs b/SNP_Evolution/Specs/Parts/HandBuiltParts.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/HandBuiltParts.cs rename to SNP_Evolution/Specs/Parts/HandBuiltParts.cs index ce20f2e..1a08b62 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/HandBuiltParts.cs +++ b/SNP_Evolution/Specs/Parts/HandBuiltParts.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Networks; -using static SnpEvolution.Evolution.Parts.ReferenceParts; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; +using static SnpEvolution.Specs.Parts.ReferenceParts; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // Each is laid out start, data in-ports, data out-ports, done ports, then its own neurons, so PortLayout.AfterInputs binds it. public static class HandBuiltParts diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/HardwareCost.cs b/SNP_Evolution/Specs/Parts/HardwareCost.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/HardwareCost.cs rename to SNP_Evolution/Specs/Parts/HardwareCost.cs index c41920e..846c66a 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/HardwareCost.cs +++ b/SNP_Evolution/Specs/Parts/HardwareCost.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // What a network costs to build in hardware, beyond its neuron count: synapses are wires, distinct rules are the // comparators a neuron needs, register width is the most spikes any neuron holds (the counter's bits), and the lasso diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/LibraryPart.cs b/SNP_Evolution/Specs/Parts/LibraryPart.cs similarity index 90% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/LibraryPart.cs rename to SNP_Evolution/Specs/Parts/LibraryPart.cs index a7e6758..52f8968 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/LibraryPart.cs +++ b/SNP_Evolution/Specs/Parts/LibraryPart.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // Where a library part came from: the seed its contract was evolved with, the run that found it, and the networks // scored to find, shrink and verify it. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleCuts.cs b/SNP_Evolution/Specs/Parts/ModuleCuts.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleCuts.cs rename to SNP_Evolution/Specs/Parts/ModuleCuts.cs index de2b874..05d4fb4 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleCuts.cs +++ b/SNP_Evolution/Specs/Parts/ModuleCuts.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Operators; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // Neurons cut out of a network with only the synapses among themselves, and the ports where they met the rest: // Inputs (indices into Body) received spikes from outside, Outputs sent spikes outside. A neuron that was the diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleEdits.cs b/SNP_Evolution/Specs/Parts/ModuleEdits.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleEdits.cs rename to SNP_Evolution/Specs/Parts/ModuleEdits.cs index 7834e24..0816c78 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleEdits.cs +++ b/SNP_Evolution/Specs/Parts/ModuleEdits.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { public static class ModuleEdits { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleLibrary.cs b/SNP_Evolution/Specs/Parts/ModuleLibrary.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleLibrary.cs rename to SNP_Evolution/Specs/Parts/ModuleLibrary.cs index 6cb659e..a45b7ca 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ModuleLibrary.cs +++ b/SNP_Evolution/Specs/Parts/ModuleLibrary.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // A part kept for reuse: a cut, where it came from, and how often putting a copy of it into a network made a // child fitter than its parent. A verified part with a contract also carries it as Part; a cheaper part with the diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/Part.cs b/SNP_Evolution/Specs/Parts/Part.cs similarity index 79% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/Part.cs rename to SNP_Evolution/Specs/Parts/Part.cs index 604b2f9..54348cd 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/Part.cs +++ b/SNP_Evolution/Specs/Parts/Part.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { public sealed record Part(Contract Contract, Network Network, PortBinding Binding) { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/PartRecipe.cs b/SNP_Evolution/Specs/Parts/PartRecipe.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/PartRecipe.cs rename to SNP_Evolution/Specs/Parts/PartRecipe.cs index 38c744e..9f83a8f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/PartRecipe.cs +++ b/SNP_Evolution/Specs/Parts/PartRecipe.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { public sealed record RecipePart(int Instance, string Contract); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/PartReuse.cs b/SNP_Evolution/Specs/Parts/PartReuse.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/PartReuse.cs rename to SNP_Evolution/Specs/Parts/PartReuse.cs index 52ff5c7..472e534 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/PartReuse.cs +++ b/SNP_Evolution/Specs/Parts/PartReuse.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Networks; +using SnpEvolution.Model; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // Unlike Module.Uses and Wins, which credit insertions during search, this counts what the final networks hold, with a promoted part's children as nested copies. public sealed record PartCount(string Contract, int Direct, int Nested) diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/PartWiring.cs b/SNP_Evolution/Specs/Parts/PartWiring.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/PartWiring.cs rename to SNP_Evolution/Specs/Parts/PartWiring.cs index eb4615e..3e2a24f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/PartWiring.cs +++ b/SNP_Evolution/Specs/Parts/PartWiring.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // Positions are 1-based network positions, in the part's own neuron order. public sealed record PartCopy(ModuleTag Tag, LibraryPart Part, IReadOnlyList Positions) diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ProvenBound.cs b/SNP_Evolution/Specs/Parts/ProvenBound.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/ProvenBound.cs rename to SNP_Evolution/Specs/Parts/ProvenBound.cs index c7c6f11..e23ecf0 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ProvenBound.cs +++ b/SNP_Evolution/Specs/Parts/ProvenBound.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // UpTo is the largest bound N for which every input with every value at most N meets the contract, -1 when not even // N = 0 is proven. AllInputs says the bound covers every input there is, as it does for a part with no data in-ports diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ReferenceParts.cs b/SNP_Evolution/Specs/Parts/ReferenceParts.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/ReferenceParts.cs rename to SNP_Evolution/Specs/Parts/ReferenceParts.cs index 574ec1c..cca21df 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/ReferenceParts.cs +++ b/SNP_Evolution/Specs/Parts/ReferenceParts.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // Hand-built parts for catalogue contracts, built and bound the way evolved parts are. public static class ReferenceParts diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Parts/StopReason.cs b/SNP_Evolution/Specs/Parts/StopReason.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Parts/StopReason.cs rename to SNP_Evolution/Specs/Parts/StopReason.cs index e210c39..b835b62 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Parts/StopReason.cs +++ b/SNP_Evolution/Specs/Parts/StopReason.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; -namespace SnpEvolution.Evolution.Parts +namespace SnpEvolution.Specs.Parts { // Why checking a part went no further. public enum Stop diff --git a/SNP_Evolution/Specs/Specs.csproj b/SNP_Evolution/Specs/Specs.csproj new file mode 100644 index 0000000..034c52e --- /dev/null +++ b/SNP_Evolution/Specs/Specs.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/AcceptorTask.cs b/SNP_Evolution/Specs/Tasks/AcceptorTask.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/AcceptorTask.cs rename to SNP_Evolution/Specs/Tasks/AcceptorTask.cs index ab9fabc..45fbdb5 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/AcceptorTask.cs +++ b/SNP_Evolution/Specs/Tasks/AcceptorTask.cs @@ -2,7 +2,7 @@ using System.Linq; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { public sealed record AcceptorExample(int Number, bool Accept); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/CloseCredit.cs b/SNP_Evolution/Specs/Tasks/CloseCredit.cs similarity index 90% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/CloseCredit.cs rename to SNP_Evolution/Specs/Tasks/CloseCredit.cs index 3265790..7515a3a 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/CloseCredit.cs +++ b/SNP_Evolution/Specs/Tasks/CloseCredit.cs @@ -1,6 +1,6 @@ using System; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // Partial credit for a number that is wrong but close, which gives the search a slope towards the right one. public static class CloseCredit diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractPorts.cs b/SNP_Evolution/Specs/Tasks/ContractPorts.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractPorts.cs rename to SNP_Evolution/Specs/Tasks/ContractPorts.cs index 6b1f15c..950f5e4 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractPorts.cs +++ b/SNP_Evolution/Specs/Tasks/ContractPorts.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // Firings come data out-ports first, then done ports, each in the contract's order. public sealed class ContractPorts diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractScoring.cs b/SNP_Evolution/Specs/Tasks/ContractScoring.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractScoring.cs rename to SNP_Evolution/Specs/Tasks/ContractScoring.cs index a13d7ab..a6f66de 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractScoring.cs +++ b/SNP_Evolution/Specs/Tasks/ContractScoring.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using SnpEvolution.Evolution.Contracts; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { public enum ContractRule { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractTask.cs b/SNP_Evolution/Specs/Tasks/ContractTask.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractTask.cs rename to SNP_Evolution/Specs/Tasks/ContractTask.cs index ae5318f..eb7177a 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/ContractTask.cs +++ b/SNP_Evolution/Specs/Tasks/ContractTask.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // Latency counts from the step the start spike reaches the part, so a done neuron fed straight from start has latency 1. public sealed class ContractTask : IContractTask, IProposing diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/FunctionTask.cs b/SNP_Evolution/Specs/Tasks/FunctionTask.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/FunctionTask.cs rename to SNP_Evolution/Specs/Tasks/FunctionTask.cs index 6cbd1b3..4f99c29 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/FunctionTask.cs +++ b/SNP_Evolution/Specs/Tasks/FunctionTask.cs @@ -3,7 +3,7 @@ using System.Linq; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { public sealed record FunctionExample(IReadOnlyList Arguments, int Result); diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/GeneratorTask.cs b/SNP_Evolution/Specs/Tasks/GeneratorTask.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/GeneratorTask.cs rename to SNP_Evolution/Specs/Tasks/GeneratorTask.cs index c660484..8a3400a 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/GeneratorTask.cs +++ b/SNP_Evolution/Specs/Tasks/GeneratorTask.cs @@ -2,7 +2,7 @@ using System.Linq; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // The network runs with no input and should generate exactly the expected set, scored by a pluggable function. public sealed class GeneratorTask : ISetTask diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/IFitnessFunction.cs b/SNP_Evolution/Specs/Tasks/IFitnessFunction.cs similarity index 86% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/IFitnessFunction.cs rename to SNP_Evolution/Specs/Tasks/IFitnessFunction.cs index 51f8d01..fb12acd 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/IFitnessFunction.cs +++ b/SNP_Evolution/Specs/Tasks/IFitnessFunction.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // Scores a network's sorted outputs from 0 (useless) to 1 (solved); Solved.Sampled applies to every function. public interface IFitnessFunction diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/ITask.cs b/SNP_Evolution/Specs/Tasks/ITask.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/ITask.cs rename to SNP_Evolution/Specs/Tasks/ITask.cs index a48aa04..92a35f9 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/ITask.cs +++ b/SNP_Evolution/Specs/Tasks/ITask.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // One input to try a network on, and what to read back. Watch names the neurons a Ports readout watches. public sealed record TaskCase(InputSpikes Input, Readout Readout, PortWatch? Watch = null) diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/JaccardFitness.cs b/SNP_Evolution/Specs/Tasks/JaccardFitness.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/JaccardFitness.cs rename to SNP_Evolution/Specs/Tasks/JaccardFitness.cs index 7eb690e..5b2b93c 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/JaccardFitness.cs +++ b/SNP_Evolution/Specs/Tasks/JaccardFitness.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // How much the distinct outputs and the expected set overlap, ignoring how often each output occurs. public sealed class JaccardFitness : IFitnessFunction diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/SequenceTask.cs b/SNP_Evolution/Specs/Tasks/SequenceTask.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/SequenceTask.cs rename to SNP_Evolution/Specs/Tasks/SequenceTask.cs index 633547b..eec0a3f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/SequenceTask.cs +++ b/SNP_Evolution/Specs/Tasks/SequenceTask.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // The network runs with no input, and the gaps between its output spikes should be the expected sequence, in // order and with repeats, such as 1,1,2,3,5,8. Spikes after the sequence are ignored, so it is a prefix to match. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/SetCoverageFitness.cs b/SNP_Evolution/Specs/Tasks/SetCoverageFitness.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/SetCoverageFitness.cs rename to SNP_Evolution/Specs/Tasks/SetCoverageFitness.cs index a789798..a5c6263 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/SetCoverageFitness.cs +++ b/SNP_Evolution/Specs/Tasks/SetCoverageFitness.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // An F1-style score against the expected set, scaled by how much of that set the outputs cover. public sealed class SetCoverageFitness : IFitnessFunction diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/Solved.cs b/SNP_Evolution/Specs/Tasks/Solved.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/Solved.cs rename to SNP_Evolution/Specs/Tasks/Solved.cs index c7b2a76..8326247 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/Solved.cs +++ b/SNP_Evolution/Specs/Tasks/Solved.cs @@ -1,4 +1,4 @@ -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // When a score counts as solved, once for each kind of score. public static class Solved diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/SpikeTrains.cs b/SNP_Evolution/Specs/Tasks/SpikeTrains.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/SpikeTrains.cs rename to SNP_Evolution/Specs/Tasks/SpikeTrains.cs index 944c00f..a74a358 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/SpikeTrains.cs +++ b/SNP_Evolution/Specs/Tasks/SpikeTrains.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // Readings of an output spike train, given as the steps the output neuron fired on. public static class SpikeTrains diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/SpikeWordTask.cs b/SNP_Evolution/Specs/Tasks/SpikeWordTask.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/SpikeWordTask.cs rename to SNP_Evolution/Specs/Tasks/SpikeWordTask.cs index 0f095ef..9b4ded0 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/SpikeWordTask.cs +++ b/SNP_Evolution/Specs/Tasks/SpikeWordTask.cs @@ -3,7 +3,7 @@ using System.Linq; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // The network runs with no input, and its output spike train should spell the expected binary word: a 1 on every // step the output neuron fires, a 0 on every step it does not, from the first step. Scored per run by balanced diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/StreamingTask.cs b/SNP_Evolution/Specs/Tasks/StreamingTask.cs similarity index 99% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/StreamingTask.cs rename to SNP_Evolution/Specs/Tasks/StreamingTask.cs index 35743d9..d70130f 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/StreamingTask.cs +++ b/SNP_Evolution/Specs/Tasks/StreamingTask.cs @@ -3,7 +3,7 @@ using System.Linq; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // What the output should do within one window of a streaming case. public enum WindowTarget diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/TaskAbilities.cs b/SNP_Evolution/Specs/Tasks/TaskAbilities.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/TaskAbilities.cs rename to SNP_Evolution/Specs/Tasks/TaskAbilities.cs index 13d503e..c267caa 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/TaskAbilities.cs +++ b/SNP_Evolution/Specs/Tasks/TaskAbilities.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // What some tasks can do beyond what every task does. A caller asks for the ability it needs, never for a kind of task. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/TaskScoring.cs b/SNP_Evolution/Specs/Tasks/TaskScoring.cs similarity index 97% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/TaskScoring.cs rename to SNP_Evolution/Specs/Tasks/TaskScoring.cs index b2740c3..0f46d51 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/TaskScoring.cs +++ b/SNP_Evolution/Specs/Tasks/TaskScoring.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { public static class TaskScoring { diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/TriggeredSequenceTask.cs b/SNP_Evolution/Specs/Tasks/TriggeredSequenceTask.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Evolution/Tasks/TriggeredSequenceTask.cs rename to SNP_Evolution/Specs/Tasks/TriggeredSequenceTask.cs index cd220cf..496fef9 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Tasks/TriggeredSequenceTask.cs +++ b/SNP_Evolution/Specs/Tasks/TriggeredSequenceTask.cs @@ -3,7 +3,7 @@ using System.Linq; using SnpEvolution.Simulation; -namespace SnpEvolution.Evolution.Tasks +namespace SnpEvolution.Specs.Tasks { // A part that continues a sequence when told to: its one input neuron receives a spike on step 0, and the gaps // between its output spikes should be the expected ones, counting from step 1, the step an input neuron that diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Verification/BoundedCheck.cs b/SNP_Evolution/Specs/Verification/BoundedCheck.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Verification/BoundedCheck.cs rename to SNP_Evolution/Specs/Verification/BoundedCheck.cs index f40e020..d4db2cd 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Verification/BoundedCheck.cs +++ b/SNP_Evolution/Specs/Verification/BoundedCheck.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Verification +namespace SnpEvolution.Specs.Verification { // Proven is how far the check got and why it stopped; Verdict passes when every input was checked, fails on a // counterexample, and is unknown when the check stopped before either. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Verification/BoundedInputs.cs b/SNP_Evolution/Specs/Verification/BoundedInputs.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Verification/BoundedInputs.cs rename to SNP_Evolution/Specs/Verification/BoundedInputs.cs index 4f254a5..459463e 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Verification/BoundedInputs.cs +++ b/SNP_Evolution/Specs/Verification/BoundedInputs.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Contracts; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Verification +namespace SnpEvolution.Specs.Verification { // The inputs a bounded check runs at each bound. public static class BoundedInputs diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Verification/CounterexampleText.cs b/SNP_Evolution/Specs/Verification/CounterexampleText.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Evolution/Verification/CounterexampleText.cs rename to SNP_Evolution/Specs/Verification/CounterexampleText.cs index 7c355b3..f6fea08 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Verification/CounterexampleText.cs +++ b/SNP_Evolution/Specs/Verification/CounterexampleText.cs @@ -3,12 +3,12 @@ using System.Globalization; using System.Linq; using System.Text; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Verification +namespace SnpEvolution.Specs.Verification { // A counterexample as the verify command and the admission log print it: the input, the rule it breaks, what the // contract expects and what was read, then the steps of the failing computation. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Verification/PartMeasurement.cs b/SNP_Evolution/Specs/Verification/PartMeasurement.cs similarity index 86% rename from SNP_Evolution/SNP_Evolution/Evolution/Verification/PartMeasurement.cs rename to SNP_Evolution/Specs/Verification/PartMeasurement.cs index 99de0a0..47c29cc 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Verification/PartMeasurement.cs +++ b/SNP_Evolution/Specs/Verification/PartMeasurement.cs @@ -1,7 +1,7 @@ -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Parts; -namespace SnpEvolution.Evolution.Verification +namespace SnpEvolution.Specs.Verification { // A network run on every case of a contract on the exhaustive engine. Latency is the slowest case's, from the step // start reaches the part to the step done fires; Behaviour is what ContractTask.Behaviour reads. Verdict passes when diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Verification/Robustness.cs b/SNP_Evolution/Specs/Verification/Robustness.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Verification/Robustness.cs rename to SNP_Evolution/Specs/Verification/Robustness.cs index a56ada7..78501af 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Verification/Robustness.cs +++ b/SNP_Evolution/Specs/Verification/Robustness.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Verification +namespace SnpEvolution.Specs.Verification { // How well a part keeps its contract when what each neuron sends along each synapse may arrive up to j steps late // (SimulationOptions.Jitter), as on asynchronous hardware: the share of sampled runs, each running every case once, in diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Verification/SpikeTrace.cs b/SNP_Evolution/Specs/Verification/SpikeTrace.cs similarity index 96% rename from SNP_Evolution/SNP_Evolution/Evolution/Verification/SpikeTrace.cs rename to SNP_Evolution/Specs/Verification/SpikeTrace.cs index f5ce596..e3cb942 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Verification/SpikeTrace.cs +++ b/SNP_Evolution/Specs/Verification/SpikeTrace.cs @@ -3,11 +3,11 @@ using System.Globalization; using System.Linq; using System.Text; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; -namespace SnpEvolution.Evolution.Verification +namespace SnpEvolution.Specs.Verification { // One neuron on one step: what it held at the start, whether it applied a rule, what it sent and what it held after delivery. public readonly record struct NeuronStep(long Held, bool Applied, long Sent, long After); @@ -66,7 +66,7 @@ public static SpikeTrace Run(Network network, InputSpikes input, int steps) // The steps a contract task watches each case for, and each case's input; ContractTask encodes them the same way. public static IReadOnlyList<(string Label, InputSpikes Input, int Steps)> Cases(Contract contract) { - var task = new Evolution.Tasks.ContractTask(contract); + var task = new Tasks.ContractTask(contract); return contract.Cases.Select((@case, index) => ($"case {index + 1} {@case.Label(contract.DataIn)}".TrimEnd(), task.Cases[index].Input, task.StepsNeeded)).ToList(); } diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Verification/Verdict.cs b/SNP_Evolution/Specs/Verification/Verdict.cs similarity index 89% rename from SNP_Evolution/SNP_Evolution/Evolution/Verification/Verdict.cs rename to SNP_Evolution/Specs/Verification/Verdict.cs index e935436..73ed47c 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Verification/Verdict.cs +++ b/SNP_Evolution/Specs/Verification/Verdict.cs @@ -1,9 +1,9 @@ -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Verification +namespace SnpEvolution.Specs.Verification { // What checking a network against a contract found: it passed, it failed on a counterexample, or nothing could be // said, and why. diff --git a/SNP_Evolution/SNP_Evolution/Evolution/Verification/Verifier.cs b/SNP_Evolution/Specs/Verification/Verifier.cs similarity index 94% rename from SNP_Evolution/SNP_Evolution/Evolution/Verification/Verifier.cs rename to SNP_Evolution/Specs/Verification/Verifier.cs index 86e4806..aa6a7c5 100644 --- a/SNP_Evolution/SNP_Evolution/Evolution/Verification/Verifier.cs +++ b/SNP_Evolution/Specs/Verification/Verifier.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Tasks; -using SnpEvolution.Networks; +using SnpEvolution.Model; using SnpEvolution.Simulation; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Tasks; -namespace SnpEvolution.Evolution.Verification +namespace SnpEvolution.Specs.Verification { // Checks a network against a contract on the exhaustive engine: the one place a network is run on a contract's cases // and judged. Part search, promotion, the part library and each bound of a bounded check go through it. A case too diff --git a/SNP_Evolution/SNP_Evolution/Storage/FitnessCsv.cs b/SNP_Evolution/Storage/FitnessCsv.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Storage/FitnessCsv.cs rename to SNP_Evolution/Storage/FitnessCsv.cs diff --git a/SNP_Evolution/SNP_Evolution/Storage/Json.cs b/SNP_Evolution/Storage/Json.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Storage/Json.cs rename to SNP_Evolution/Storage/Json.cs index efe5ff3..6279857 100644 --- a/SNP_Evolution/SNP_Evolution/Storage/Json.cs +++ b/SNP_Evolution/Storage/Json.cs @@ -1,7 +1,7 @@ using System.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Storage { diff --git a/SNP_Evolution/SNP_Evolution/Storage/NetworkFiles.cs b/SNP_Evolution/Storage/NetworkFiles.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Storage/NetworkFiles.cs rename to SNP_Evolution/Storage/NetworkFiles.cs index ce40c71..8ce2629 100644 --- a/SNP_Evolution/SNP_Evolution/Storage/NetworkFiles.cs +++ b/SNP_Evolution/Storage/NetworkFiles.cs @@ -1,7 +1,7 @@ using System; using System.IO; using Newtonsoft.Json; -using SnpEvolution.Networks; +using SnpEvolution.Model; namespace SnpEvolution.Storage { diff --git a/SNP_Evolution/SNP_Evolution/Storage/PartLibraryFiles.cs b/SNP_Evolution/Storage/PartLibraryFiles.cs similarity index 98% rename from SNP_Evolution/SNP_Evolution/Storage/PartLibraryFiles.cs rename to SNP_Evolution/Storage/PartLibraryFiles.cs index 8f254de..d6ceb08 100644 --- a/SNP_Evolution/SNP_Evolution/Storage/PartLibraryFiles.cs +++ b/SNP_Evolution/Storage/PartLibraryFiles.cs @@ -3,11 +3,11 @@ using System.IO; using System.Linq; using Newtonsoft.Json; -using SnpEvolution.Evolution.Accounting; -using SnpEvolution.Evolution.Contracts; -using SnpEvolution.Evolution.Parts; -using SnpEvolution.Evolution.Verification; -using SnpEvolution.Networks; +using SnpEvolution.Model; +using SnpEvolution.Specs.Accounting; +using SnpEvolution.Specs.Contracts; +using SnpEvolution.Specs.Parts; +using SnpEvolution.Specs.Verification; namespace SnpEvolution.Storage { diff --git a/SNP_Evolution/SNP_Evolution/Storage/SpikeCountJsonConverter.cs b/SNP_Evolution/Storage/SpikeCountJsonConverter.cs similarity index 100% rename from SNP_Evolution/SNP_Evolution/Storage/SpikeCountJsonConverter.cs rename to SNP_Evolution/Storage/SpikeCountJsonConverter.cs diff --git a/SNP_Evolution/SNP_Evolution/Storage/StopReasonJsonConverter.cs b/SNP_Evolution/Storage/StopReasonJsonConverter.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution/Storage/StopReasonJsonConverter.cs rename to SNP_Evolution/Storage/StopReasonJsonConverter.cs index 839bb85..ba6d4b3 100644 --- a/SNP_Evolution/SNP_Evolution/Storage/StopReasonJsonConverter.cs +++ b/SNP_Evolution/Storage/StopReasonJsonConverter.cs @@ -1,6 +1,6 @@ using System; using Newtonsoft.Json; -using SnpEvolution.Evolution.Parts; +using SnpEvolution.Specs.Parts; namespace SnpEvolution.Storage { diff --git a/SNP_Evolution/Storage/Storage.csproj b/SNP_Evolution/Storage/Storage.csproj new file mode 100644 index 0000000..87ba781 --- /dev/null +++ b/SNP_Evolution/Storage/Storage.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/install-snp.sh b/install-snp.sh index ba76398..310181b 100755 --- a/install-snp.sh +++ b/install-snp.sh @@ -14,7 +14,7 @@ version="1.0.0-local.$(date +%Y%m%d%H%M%S)" echo "Building SNP_Evolution $version..." rm -rf nupkg -dotnet pack SNP_Evolution -c Release -p:Version="$version" --nologo -v quiet +dotnet pack Cli -c Release -p:Version="$version" --nologo -v quiet if dotnet tool list --global | grep -qi '^snp_evolution '; then dotnet tool uninstall --global SNP_Evolution >/dev/null From ab871510aa0a66227fd198f9e765de79097c07fe Mon Sep 17 00:00:00 2001 From: ethiril Date: Wed, 7 Oct 2026 20:25:51 +0100 Subject: [PATCH 2/2] Tighten passs --- .github/tickets/where.py | 15 ++- .idea/.gitignore | 10 ++ .idea/SNP_Evolution.iml | 9 ++ .idea/material_theme_project_new.xml | 13 +++ .idea/misc.xml | 6 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ SNP_Evolution/Cli/Cli.csproj | 1 + SNP_Evolution/Directory.Build.props | 3 +- .../Application/BenchmarkServiceTests.cs | 2 +- .../Application/CatalogTests.cs | 2 +- .../Application/EvaluationAccountingTests.cs | 81 ++++++++++++++++- ...ssionTests.cs => EvolutionSessionTests.cs} | 76 ++++++---------- .../Application/EvolveServiceTests.cs | 2 +- .../Application/NetworkRunServiceTests.cs | 2 +- .../Application/OutputTargetTests.cs | 1 - .../Application/PartLibrariesTests.cs | 2 +- .../Application/PartsServiceTests.cs | 22 +++-- .../Application/ReachServiceTests.cs | 2 +- .../Application/RunAdvisorTests.cs | 16 +--- .../Application/RunLayersTests.cs | 41 +++++++++ .../Application/TargetPatternsTests.cs | 2 +- .../Cli/BenchmarkCommandTests.cs | 20 ++++ .../Cli/CommandArgsTests.cs | 14 ++- .../Cli/EvolvePartsCommandTests.cs | 26 ++++++ .../Cli/ExportCommandsTests.cs | 33 ++----- .../Cli/ListCommandsTests.cs | 1 - .../Cli/VerifyCommandTests.cs | 6 +- .../Compilation/RecurrenceCompilerTests.cs | 6 +- .../RegisterMachineCompilerTests.cs | 6 +- .../Compilation/RegisterProgramTests.cs | 6 +- .../Export/NirExporterTests.cs | 32 +++---- .../Export/UppaalExporterTests.cs | 31 ++++--- .../Export/VerilogExporterTests.cs | 30 ++---- .../Fixtures/BenchmarkFixtures.cs | 3 + .../Fixtures/CatalogEntries.cs | 11 +++ .../{Golden => Fixtures}/CommandRun.cs | 6 +- .../Fixtures/CompositionFixtures.cs | 32 ++++++- .../Fixtures/ConsoleCapture.cs | 3 +- .../Fixtures/ConsoleCaptureTests.cs | 24 +++++ .../SNP_Evolution.Tests/Fixtures/Engines.cs | 12 --- .../SNP_Evolution.Tests/Fixtures/Factories.cs | 6 +- .../Fixtures/ModuleFixtures.cs | 8 -- .../Fixtures/PartFixtures.cs | 28 +++++- .../Fixtures/PortTrials.cs | 22 +++++ .../Fixtures/RepositoryFiles.cs | 2 + .../SNP_Evolution.Tests/Fixtures/Runs.cs | 8 +- .../SNP_Evolution.Tests/Fixtures/Sequences.cs | 3 + .../Fixtures/TempFolderTests.cs | 17 ++++ .../Fixtures/TestNetworks.cs | 13 ++- .../Fixtures/WellFormedNetworks.cs | 2 +- .../Golden/CommandGoldenTests.cs | 6 +- .../Golden/EngineTraceGoldenTests.cs | 51 +---------- .../SNP_Evolution.Tests/Golden/GoldenFile.cs | 1 - .../SNP_Evolution.Tests/Golden/GoldenParts.cs | 13 +++ .../Golden/SpikeTraceGoldenTests.cs | 50 ++++++++++ .../SNP_Evolution.Tests/Layering/Layers.cs | 11 ++- .../Layering/MethodBodyTypes.cs | 86 ++++++++++++++++++ .../Layering/MethodBodyTypesTests.cs | 19 ++++ .../Layering/NamespaceGraph.cs | 82 +---------------- .../Layering/ProjectLayerTests.cs | 15 ++- .../Model/NetworkNotationTests.cs | 23 +++++ .../SNP_Evolution.Tests/Model/NetworkTests.cs | 30 ++++++ .../SNP_Evolution.Tests/Model/RuleTests.cs | 22 +++-- .../Model/StandardRuleTests.cs | 41 --------- .../Search/Algorithms/GenerationLoopTests.cs | 37 ++++++++ .../Algorithms/GeneticAlgorithmTests.cs | 1 - .../Search/Algorithms/ImmigrantsTests.cs | 68 +++++++------- .../Search/Algorithms/StallDetectorTests.cs | 29 ------ .../Benchmarking/AlgorithmSelectorTests.cs | 24 +++++ .../Search/Benchmarking/BenchmarkTests.cs | 13 +-- .../Search/Benchmarking/TaskSuiteTests.cs | 19 ++++ ...earchTests.cs => CompositionSpaceTests.cs} | 37 +------- .../Search/EvolutionContextTests.cs | 40 +++----- .../Search/EvolutionSearchTests.cs | 9 +- .../Search/Fitness/FitnessEvaluatorTests.cs | 60 ++++++------ .../Search/Genome/ExpressionGeneratorTests.cs | 29 ++++++ .../Search/Genome/NetworkFactoryTests.cs | 18 +++- .../Search/Genome/NetworkGenerationTests.cs | 63 ------------- .../Search/Genome/RandomTopologyTests.cs | 28 ++++++ .../Search/Modules/CompositionEditsTests.cs | 30 ++++++ .../Search/Modules/HandBuiltMachinesTests.cs | 27 +++--- .../Search/Modules/ModularEvolutionTests.cs | 76 +++++++++------- .../Search/Modules/ModuleMutationsTests.cs | 33 +++++-- .../Search/Modules/PartMutationsTests.cs | 53 ++++++++--- .../Search/Modules/PromotionTests.cs | 91 ++----------------- .../Operators/StructuralMutationsTests.cs | 3 +- .../Search/PartSearchTests.cs | 11 ++- .../Search/ProgramEditsTests.cs | 17 ++++ .../Search/ProgramScoringTests.cs | 26 ++++++ .../Search/ProgramSearchTests.cs | 26 ------ .../Search/Proposals/PartProposalsTests.cs | 38 ++++---- .../Proposals/RecurrenceProposerTests.cs | 8 +- .../Search/SearchCatalogTests.cs | 13 --- .../Search/ShrinkSearchTests.cs | 2 +- .../Simulation/AxonalDelayTests.cs | 14 +-- .../Simulation/ExhaustiveEngineTests.cs | 21 +++-- .../Simulation/Metal/MetalEngineTests.cs | 11 +++ .../Simulation/NetworkRunnerTests.cs | 24 ++--- .../Simulation/NetworkSimulationTests.cs | 20 ++++ .../Simulation/PortReadoutTests.cs | 35 ++----- .../Simulation/SimulationEngineTests.cs | 27 +----- .../Simulation/StandardSemanticsTests.cs | 6 +- .../Specs/Accounting/EvaluationBudgetTests.cs | 76 ---------------- .../Specs/Contracts/ArithmeticPartsTests.cs | 23 ----- .../Specs/Contracts/FirstPartsTests.cs | 40 ++------ .../Specs/Parts/CompositionTests.cs | 8 +- .../Specs/Parts/FibonacciCompositionTests.cs | 36 ++++---- .../Specs/Parts/HandBuiltPartsTests.cs | 20 ++++ .../Specs/Parts/HardwareCostTests.cs | 21 +++-- .../Specs/Parts/ModuleCutTests.cs | 9 ++ .../Specs/Parts/ModuleEditTests.cs | 28 ------ .../Specs/Parts/ModuleLibraryTests.cs | 64 +++++++++++++ .../Specs/Parts/PartLibraryTests.cs | 82 ----------------- .../Specs/Parts/PartWiringTests.cs | 11 +-- .../Specs/Tasks/ContractTaskTests.cs | 4 +- .../Specs/Tasks/SequenceTaskTests.cs | 18 ++-- .../Specs/Tasks/SpikeWordTaskTests.cs | 8 ++ .../Specs/Tasks/StreamingTaskTests.cs | 32 +++---- .../Specs/Tasks/TaskScoringTests.cs | 4 +- .../Specs/Verification/BoundedCheckTests.cs | 22 ----- .../Specs/Verification/BoundedInputsTests.cs | 19 ++++ .../Specs/Verification/SpikeTraceTests.cs | 17 ++++ .../Storage/NetworkFilesTests.cs | 17 +++- .../Storage/PartLibraryFilesTests.cs | 79 ++++++++++++++-- 125 files changed, 1622 insertions(+), 1286 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/SNP_Evolution.iml create mode 100644 .idea/material_theme_project_new.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml rename SNP_Evolution/SNP_Evolution.Tests/Application/{ComposeSessionTests.cs => EvolutionSessionTests.cs} (70%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Application/RunLayersTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Cli/BenchmarkCommandTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Cli/EvolvePartsCommandTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/CatalogEntries.cs rename SNP_Evolution/SNP_Evolution.Tests/{Golden => Fixtures}/CommandRun.cs (95%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/ConsoleCaptureTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/Engines.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/PortTrials.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Fixtures/TempFolderTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Golden/GoldenParts.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Golden/SpikeTraceGoldenTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Layering/MethodBodyTypes.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Layering/MethodBodyTypesTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Model/NetworkTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Algorithms/GenerationLoopTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Benchmarking/AlgorithmSelectorTests.cs rename SNP_Evolution/SNP_Evolution.Tests/Search/{CompositionSearchTests.cs => CompositionSpaceTests.cs} (75%) create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Genome/ExpressionGeneratorTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Genome/NetworkGenerationTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Genome/RandomTopologyTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/Modules/CompositionEditsTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/ProgramEditsTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Search/ProgramScoringTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/HandBuiltPartsTests.cs delete mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Parts/PartLibraryTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Verification/BoundedInputsTests.cs create mode 100644 SNP_Evolution/SNP_Evolution.Tests/Specs/Verification/SpikeTraceTests.cs diff --git a/.github/tickets/where.py b/.github/tickets/where.py index 013245d..80a249c 100644 --- a/.github/tickets/where.py +++ b/.github/tickets/where.py @@ -4,7 +4,8 @@ python3 .github/tickets/where.py open issues, asked of GitHub with gh python3 .github/tickets/where.py 46 112 only these issue numbers -A path is a backticked token holding a slash or a file extension. It is looked up from the solution folder +A path is a backticked token holding a slash or ending in a file extension the repository uses, so dotted type and +member names are not mistaken for paths. It is looked up from the solution folder `SNP_Evolution/` first, as the tickets README says, then from the repository root (for `tools/`, `README.md` and the like). A path written after "new" is one the ticket creates, so only the folder it goes in must exist. """ @@ -14,6 +15,8 @@ import subprocess import sys +EXTENSIONS = r"\.(cs|csproj|props|slnx|md|py|sh|metal|json|yml)$" + ROOT = pathlib.Path(__file__).resolve().parents[2] SOLUTION = ROOT / "SNP_Evolution" TICKETS = ROOT / ".github" / "tickets" @@ -34,9 +37,9 @@ def tickets(numbers): def paths(where): - for match in re.finditer(r"(\bnew\s+)?`([^`]+)`", where): + for match in re.finditer(r"(\bnew\s+(?:folder\s+)?)?`([^`]+)`", where): token = match.group(2) - if "/" in token or re.search(r"\.\w+$", token): + if "/" in token or re.search(EXTENSIONS, token): yield token, match.group(1) is not None @@ -54,8 +57,10 @@ def main(): numbers = {int(arg) for arg in sys.argv[1:]} or open_issues() missing = 0 checked = 0 + found = 0 for number, path, text in tickets(numbers): - where = re.search(r"^Where:(.*)$", text, re.M) + found += 1 + where = re.search(r"^Where:(.*?)(?:\n\s*\n|\Z)", text, re.M | re.S) if not where: continue for token, new in paths(where.group(1)): @@ -63,7 +68,7 @@ def main(): if not exists(token, new): missing += 1 print(f"#{number} {path.relative_to(ROOT)}: {'new ' if new else ''}{token}") - print(f"{checked} paths in {len(numbers)} tickets, {missing} missing") + print(f"{checked} paths in {found} tickets, {missing} missing") return 1 if missing else 0 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/SNP_Evolution.iml b/.idea/SNP_Evolution.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/SNP_Evolution.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 0000000..0551ab0 --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..73155d4 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..58f75c8 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SNP_Evolution/Cli/Cli.csproj b/SNP_Evolution/Cli/Cli.csproj index 25b4f71..062448a 100644 --- a/SNP_Evolution/Cli/Cli.csproj +++ b/SNP_Evolution/Cli/Cli.csproj @@ -3,6 +3,7 @@ Exe 1.0.0 + true true snp-evolution SNP_Evolution diff --git a/SNP_Evolution/Directory.Build.props b/SNP_Evolution/Directory.Build.props index a159723..cf5682b 100644 --- a/SNP_Evolution/Directory.Build.props +++ b/SNP_Evolution/Directory.Build.props @@ -1,12 +1,13 @@ - + net10.0 14.0 enable enable true + false SnpEvolution.$(MSBuildProjectName) SnpEvolution.$(MSBuildProjectName) diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/BenchmarkServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/BenchmarkServiceTests.cs index 539ff84..55d7c04 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/BenchmarkServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/BenchmarkServiceTests.cs @@ -18,7 +18,7 @@ public void OnlyARacingCompositionSearchReadsThePartLibrary() Assert.StartsWith("Composition search builds from 0 part(s)", BenchmarkService.Plan(settings, settings.BenchmarkSettings, composing).Value!.PartsNote); temp.Made(); - File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); + PartFixtures.WriteBrokenLibrary(folder); Assert.Contains("delay-2.json", BenchmarkService.Plan(settings, settings.BenchmarkSettings, composing).Error); } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/CatalogTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/CatalogTests.cs index f12499a..752f30d 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/CatalogTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/CatalogTests.cs @@ -17,7 +17,7 @@ public void AnExactNameWinsOverNamesThatOnlyContainIt() [Fact] public void TheMenuOffersEveryGeneticAlgorithmInTheCatalog() { - Assert.Equal(SearchCatalog.Evolution, SnpEvolution.Application.Catalog.Algorithms); + Assert.Equal(SearchCatalog.Evolution, Catalog.Algorithms); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/EvaluationAccountingTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/EvaluationAccountingTests.cs index 7371473..fa4e401 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/EvaluationAccountingTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/EvaluationAccountingTests.cs @@ -1,7 +1,9 @@ using SnpEvolution.Application; using SnpEvolution.Search; using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Benchmarking; using SnpEvolution.Search.Modules; +using SnpEvolution.Simulation; using SnpEvolution.Specs.Accounting; using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; @@ -12,12 +14,31 @@ public class EvaluationAccountingTests { private const int Population = 10; + // An evaluator reuses the same case objects for every network, so trials sharing the first trial's input are one per network. + private sealed class CountingEngine : ISimulationEngine + { + private readonly ISimulationEngine inner; + private long networks; + + public CountingEngine(ISimulationEngine inner) => this.inner = inner; + + public long Networks => Interlocked.Read(ref networks); + + public EngineSupport Support => inner.Support; + + public IReadOnlyList Run(IReadOnlyList trials, SimulationOptions options, Random random) + { + Interlocked.Add(ref networks, trials.Count(trial => ReferenceEquals(trial.Input, trials[0].Input))); + return inner.Run(trials, options, random); + } + } + // A target no small network makes in a few generations, so the run stalls and the modular loop reacts. private static Settings Stalling(int generations) => new Settings { - Target = new OutputTarget(TargetKind.Sequence, new[] { 1, 1, 2, 3, 5, 8, 13, 21, 34 }), + Target = new OutputTarget(TargetKind.Sequence, Sequences.Fibonacci.Take(9).ToArray()), Task = Catalog.TargetTask, - Engine = Catalog.Engines.Single(engine => engine.Name == "CPU, single thread"), + Engine = CatalogEntries.SingleThreadEngine, Repetitions = 2, PopulationSize = Population, MaxGenerations = generations, @@ -55,7 +76,8 @@ public void ARunStopsOnceItsEvaluationBudgetIsSpent() EvolutionSession.Evolve(settings, settings.SelectedTask, factory => factory.NewNetwork(), new Random(3), _ => { }, evaluations); - Assert.InRange(evaluations.Networks, 300, 300 + Population * (1 + new ModulePolicy().SideGenerations + new ModulePolicy().IncubationGenerations)); + var modules = new ModulePolicy(); + Assert.InRange(evaluations.Networks, 300, 300 + Population * (1 + modules.SideGenerations + modules.IncubationGenerations)); } // Composition search reads its parts from the library folder, and their recorded cost is reported as paid up front. @@ -80,5 +102,58 @@ public void ACompositionRunCountsTheLibrarysPartsAsAnUpFrontCost() Assert.Contains(run.Population, individual => individual.Genes.Neurons.Any(neuron => neuron.Module != null)); Assert.Contains("1,234", evaluations.Report().Describe()); } + + [Fact] + [Slow] + public void EveryNetworkAModularRunScoresIsCharged() + { + var engine = new CountingEngine(new SequentialCpuEngine()); + var settings = new Settings + { + Target = new OutputTarget(TargetKind.Sequence, Sequences.Fibonacci.Take(9).ToArray()), + Task = Catalog.TargetTask, + Engine = new CatalogEntry("counting", _ => engine), + Repetitions = 2, + PopulationSize = 10, + MaxGenerations = 60, + IterativeEvolution = false, + StagnationPatience = 10, + Modules = true, + }; + var budget = new EvaluationBudget(); + + EvolutionSession.Evolve(settings, settings.SelectedTask, factory => factory.NewNetwork(), new Random(3), _ => { }, budget); + + Assert.True(budget[EvaluationSource.SideRun] > 0); + Assert.Equal(engine.Networks, budget.Networks); + } + + [Fact] + [Slow] + public void EveryNetworkABenchmarkRunScoresIsChargedRetestsIncluded() + { + var engine = new CountingEngine(new ExhaustiveCpuEngine()); + BenchmarkSettings settings = BenchmarkSettings.Default with { Seeds = 1, CreateEngine = () => engine }; + + RunOutcome outcome = Benchmark.RunOnce(Catalog.StructuralDefault, TaskSuite.Functions.First(task => task.Name == "Compute n"), seed: 1, budget: 4000, settings); + + Assert.True(outcome.Solved); + Assert.Equal(engine.Networks, outcome.Evaluations); + } + + [Fact] + [Slow] + public void EveryNetworkAPartSearchScoresIsChargedAndItsChecksBesideThem() + { + var engine = new CountingEngine(new ExhaustiveCpuEngine()); + var settings = new PartSearchSettings(5_000, 1_000, 30, Catalog.StructuralDefault, () => engine); + + PartOutcome outcome = PartSearch.Evolve(PartFixtures.DelayContract(2), 1, settings, new EvaluationBudget(), _ => { }); + + Assert.True(outcome.Solved); + Assert.Equal(engine.Networks, outcome.Spent.Networks); + Assert.True(outcome.Spent[EvaluationKind.ExhaustiveCheck] > 0); + Assert.Equal(outcome.Spent.Networks + outcome.Spent[EvaluationKind.ExhaustiveCheck], outcome.Evaluations); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/ComposeSessionTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/EvolutionSessionTests.cs similarity index 70% rename from SNP_Evolution/SNP_Evolution.Tests/Application/ComposeSessionTests.cs rename to SNP_Evolution/SNP_Evolution.Tests/Application/EvolutionSessionTests.cs index 3e5c37d..d5a85f6 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/ComposeSessionTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/EvolutionSessionTests.cs @@ -14,7 +14,7 @@ namespace SnpEvolution.Tests.Application { - public sealed class ComposeSessionTests : IDisposable + public sealed class EvolutionSessionTests : IDisposable { private readonly TempFolder temp = new TempFolder("snp-compose"); private readonly List log = new List(); @@ -28,7 +28,7 @@ public sealed class ComposeSessionTests : IDisposable Task = task, RuleForm = RuleForm.Standard, OutputTiming = OutputTiming.Interval, - Engine = Catalog.Engines.Single(engine => engine.Name == "CPU, single thread"), + Engine = CatalogEntries.SingleThreadEngine, Algorithm = SearchCatalog.CompositionMapElites, Repetitions = 2, PopulationSize = 40, @@ -42,7 +42,19 @@ private IGeneticAlgorithm Run(Settings settings, int seed, EvaluationBudget? eva private static CatalogEntry SuiteTask(string name) => Catalog.Tasks.Single(task => task.Name == name); - // The done-when through the session the compose command runs: solved, promoted, saved, and the add loop reported as reused. + private Settings OneIncrementTowardsAGap() + { + var library = new ModuleLibrary(); + library.AddPart(PartFixtures.Verified(ReferenceParts.Increment()), "a test"); + PartLibraryFiles.Save(library, Path.Combine(folder, "parts")); + Settings settings = Composing(Catalog.TargetTask); + settings.Target = new OutputTarget(TargetKind.Sequence, new[] { 1, 6, 1 }); + settings.IterativeEvolution = false; + settings.PopulationSize = 12; + settings.StagnationPatience = 4; + return settings; + } + [Fact] [Slow] public void ASolvedContractIsPromotedSavedAndItsReuseReported() @@ -67,15 +79,8 @@ public void ASolvedContractIsPromotedSavedAndItsReuseReported() [Slow] public void TheRunLogListsEachProposalWithItsOutcome() { - var library = new ModuleLibrary(); - library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - PartLibraryFiles.Save(library, Path.Combine(folder, "parts")); - Settings settings = Composing(Catalog.TargetTask); - settings.Target = new OutputTarget(TargetKind.Sequence, new[] { 1, 6, 1 }); - settings.IterativeEvolution = false; - settings.PopulationSize = 12; + Settings settings = OneIncrementTowardsAGap(); settings.MaxGenerations = 40; - settings.StagnationPatience = 4; settings.ProposalBudget = 5_000; var evaluations = new EvaluationBudget(); @@ -88,51 +93,28 @@ public void TheRunLogListsEachProposalWithItsOutcome() Assert.Matches(@"generation \d+: delay \d+ \(failing checks, nothing passes gap \d+ \(\d+\)\): (solved in \d+ evaluations, kept as module \d+|not solved)", parts); } - [Fact] - public void AContractRunJustShortOfAPerfectScoreIsNotSolved() - { - var individual = new Individual(TestNetworks.AlwaysOutputsOne()); - individual.Record(new FitnessResult(0.99f, Array.Empty(), "", Exact: true)); - var run = new FixedPopulation(individual); - - Assert.True(RunLayers.IsSolved(run)); - Assert.False(RunLayers.IsSolved(run, new ContractTask(PartFixtures.RegisterContract()))); - } - - private sealed class FixedPopulation(Individual best) : IGeneticAlgorithm - { - public IReadOnlyList Population => new[] { best }; - - public int Generation => 1; - - public Individual? Best => best; - - public IReadOnlyList> FitnessHistory => Array.Empty>(); - - public void NextGeneration() { } - - public void Immigrate(IReadOnlyList newcomers) { } - - public void Rescore() { } - } - [Fact] public void ProposalsCanBeTurnedOff() { - var library = new ModuleLibrary(); - library.AddPart(Verified(ReferenceParts.Increment()), "a test"); - PartLibraryFiles.Save(library, Path.Combine(folder, "parts")); - Settings settings = Composing(Catalog.TargetTask); - settings.Target = new OutputTarget(TargetKind.Sequence, new[] { 1, 6, 1 }); - settings.IterativeEvolution = false; - settings.PopulationSize = 12; + Settings settings = OneIncrementTowardsAGap(); settings.MaxGenerations = 20; - settings.StagnationPatience = 4; settings.ProposeParts = false; IGeneticAlgorithm run = Run(settings, seed: 3); Assert.Empty(RunLayers.Proposals(run)!.Proposals); } + + [Fact] + public void OnlyOrderedTargetsEvolveIteratively() + { + var sequence = new Settings { Target = new OutputTarget(TargetKind.Sequence, new[] { 1, 1, 2, 3, 5, 8 }), Task = Catalog.TargetTask }; + var set = new Settings { Target = OutputTarget.Set(new[] { 2, 4, 6, 8 }), Task = Catalog.TargetTask }; + + Assert.True(EvolutionSession.IsIterative(sequence, sequence.SelectedTask, out _)); + Assert.False(EvolutionSession.IsIterative(set, set.SelectedTask, out _)); + sequence.IterativeEvolution = false; + Assert.False(EvolutionSession.IsIterative(sequence, sequence.SelectedTask, out _)); + } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/EvolveServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/EvolveServiceTests.cs index b231543..ffb94f4 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/EvolveServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/EvolveServiceTests.cs @@ -10,7 +10,7 @@ public void ACompositionRunOnABrokenLibraryIsATypedErrorNamingTheFile() { using var temp = new TempFolder("snp-evolve").Made(); string folder = temp.Path; - File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); + PartFixtures.WriteBrokenLibrary(folder); var settings = new Settings { Algorithm = SearchCatalog.CompositionTournament, PartLibraryFolder = folder }; EvolveResult result = EvolveService.Run(new EvolveRequest(settings, new Random(1), "Composed", Folder: folder), _ => { }); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/NetworkRunServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/NetworkRunServiceTests.cs index 118a765..d1a7b9a 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/NetworkRunServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/NetworkRunServiceTests.cs @@ -4,7 +4,7 @@ namespace SnpEvolution.Tests.Application { public class NetworkRunServiceTests { - private static Settings SingleThread() => new Settings { Engine = Catalog.Engines.Single(engine => engine.Name == "CPU, single thread") }; + private static Settings SingleThread() => new Settings { Engine = CatalogEntries.SingleThreadEngine }; [Fact] public void ANetworkWithAnInputIsScoredOnTheSelectedTask() diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/OutputTargetTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/OutputTargetTests.cs index 6942c9d..7b4a6eb 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/OutputTargetTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/OutputTargetTests.cs @@ -44,6 +44,5 @@ public void EachKindMakesItsTask() Assert.IsType(new OutputTarget(TargetKind.Sequence, new[] { 1, 1, 2 }).CreateTask(fitness)); Assert.IsType(new OutputTarget(TargetKind.BinaryWord, new[] { 0, 1 }).CreateTask(fitness)); } - } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/PartLibrariesTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/PartLibrariesTests.cs index 6a5aafb..2c6af1f 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/PartLibrariesTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/PartLibrariesTests.cs @@ -25,7 +25,7 @@ public void ABrokenLibraryIsATypedErrorNamingTheFile() { using var temp = new TempFolder("snp-broken").Made(); string folder = temp.Path; - File.WriteAllText(Path.Combine(folder, "delay-2.json"), "{ not json"); + PartFixtures.WriteBrokenLibrary(folder); Loaded loaded = PartLibraries.Load(folder, handBuilt: false, addLoop: false); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/PartsServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/PartsServiceTests.cs index 93d64d3..85958fa 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/PartsServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/PartsServiceTests.cs @@ -6,23 +6,20 @@ namespace SnpEvolution.Tests.Application { public sealed class PartsServiceTests : IDisposable { + private static readonly IReadOnlyList Delays = new[] { FirstParts.Named("delay 2"), FirstParts.Named("delay 4") }; + private readonly TempFolder temp = new TempFolder("parts-session"); private string folder => temp.Path; public void Dispose() => temp.Dispose(); - private static readonly IReadOnlyList Delays = new[] { FirstParts.Named("delay 2"), FirstParts.Named("delay 4") }; - private Settings Library(string library) => new Settings { PartBudget = 4000, PartLibraryFolder = Path.Combine(folder, library) }; private static PartsRequest Request(bool redo = false) => new PartsRequest(Delays, Seed: 1, Redo: redo); - private int Run(string library, Action log, bool redo = false) - { - PartsResult result = PartsService.Run(Library(library), Request(redo), log); - return result.Error != null ? 1 : result.AllSolved ? 0 : 2; - } + private PartsResult Run(string library, Action log, bool redo = false) => + PartsService.Run(Library(library), Request(redo), log); private static Dictionary Files(string library) => Directory.GetFiles(library).ToDictionary(path => Path.GetFileName(path), File.ReadAllText); @@ -33,8 +30,13 @@ public void TheSameSeedWritesTheSameLibrary() { var log = new List(); - Assert.Equal(0, Run("first", log.Add)); - Assert.Equal(0, Run("second", _ => { })); + PartsResult firstRun = Run("first", log.Add); + PartsResult secondRun = Run("second", _ => { }); + + Assert.Null(firstRun.Error); + Assert.True(firstRun.AllSolved); + Assert.Null(secondRun.Error); + Assert.True(secondRun.AllSolved); Dictionary first = Files(Path.Combine(folder, "first")); Assert.Equal(new[] { "delay-2.json", "delay-4.json" }, first.Keys.Order()); @@ -73,7 +75,7 @@ public void ABrokenLibraryIsReportedAndNothingIsEvolved() { string library = Path.Combine(folder, "broken"); Directory.CreateDirectory(library); - File.WriteAllText(Path.Combine(library, "delay-2.json"), "{ not json"); + PartFixtures.WriteBrokenLibrary(library); PartsResult result = PartsService.Run(Library("broken"), Request(), _ => { }); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/ReachServiceTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/ReachServiceTests.cs index 07f0373..20f98b5 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/ReachServiceTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/ReachServiceTests.cs @@ -11,7 +11,7 @@ public class ReachServiceTests private static Settings Budgeted(string folder, long budget) => new Settings { - Target = new OutputTarget(TargetKind.Sequence, new[] { 1, 1, 2, 3, 5, 8, 13 }), + Target = new OutputTarget(TargetKind.Sequence, Sequences.Fibonacci.Take(7).ToArray()), Task = Catalog.TargetTask, Repetitions = 3, PopulationSize = Population, diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/RunAdvisorTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/RunAdvisorTests.cs index 1e2926b..fdf39b9 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/RunAdvisorTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/RunAdvisorTests.cs @@ -12,7 +12,7 @@ private static Settings ForSequence(params int[] values) => [Fact] public void SuggestsFixingAFibonacciTypo() { - Settings settings = ForSequence(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 114, 233); + Settings settings = ForSequence(Sequences.FibonacciWithTypo.Take(13).ToArray()); Suggestion? typo = For(RunAdvisor.Advise(settings), "Target"); typo!.Apply(settings); @@ -36,7 +36,7 @@ public void SuggestsAUsefulPopulationAndIterativeEvolution() [Fact] public void HasNothingMoreToSayOnceItsAdviceIsTaken() { - Settings settings = ForSequence(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 114, 233); + Settings settings = ForSequence(Sequences.FibonacciWithTypo.Take(13).ToArray()); settings.PopulationSize = 4; settings.MaxNeurons = 100; settings.StagnationRecovery = false; @@ -69,17 +69,5 @@ public void AdvisingDoesNotChangeTheSettings() Assert.Equal(4, settings.PopulationSize); } - - [Fact] - public void OnlyOrderedTargetsEvolveIteratively() - { - Settings sequence = ForSequence(1, 1, 2, 3, 5, 8); - var set = new Settings { Target = OutputTarget.Set(new[] { 2, 4, 6, 8 }), Task = Catalog.TargetTask }; - - Assert.True(EvolutionSession.IsIterative(sequence, sequence.SelectedTask, out _)); - Assert.False(EvolutionSession.IsIterative(set, set.SelectedTask, out _)); - sequence.IterativeEvolution = false; - Assert.False(EvolutionSession.IsIterative(sequence, sequence.SelectedTask, out _)); - } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/RunLayersTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/RunLayersTests.cs new file mode 100644 index 0000000..c6ccc16 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/RunLayersTests.cs @@ -0,0 +1,41 @@ +using SnpEvolution.Application; +using SnpEvolution.Model; +using SnpEvolution.Search; +using SnpEvolution.Search.Algorithms; +using SnpEvolution.Search.Fitness; +using SnpEvolution.Search.Genome; +using SnpEvolution.Specs.Tasks; + +namespace SnpEvolution.Tests.Application +{ + public class RunLayersTests + { + [Fact] + public void AContractRunJustShortOfAPerfectScoreIsNotSolved() + { + var individual = new Individual(TestNetworks.AlwaysOutputsOne()); + individual.Record(new FitnessResult(0.99f, Array.Empty(), "", Exact: true)); + var run = new FixedPopulation(individual); + + Assert.True(RunLayers.IsSolved(run)); + Assert.False(RunLayers.IsSolved(run, new ContractTask(PartFixtures.RegisterContract()))); + } + + private sealed class FixedPopulation(Individual best) : IGeneticAlgorithm + { + public IReadOnlyList Population => new[] { best }; + + public int Generation => 1; + + public Individual? Best => best; + + public IReadOnlyList> FitnessHistory => Array.Empty>(); + + public void NextGeneration() { } + + public void Immigrate(IReadOnlyList newcomers) { } + + public void Rescore() { } + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Application/TargetPatternsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Application/TargetPatternsTests.cs index 531846a..1754756 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Application/TargetPatternsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Application/TargetPatternsTests.cs @@ -7,7 +7,7 @@ public class TargetPatternsTests [Fact] public void FindsTheTypoInAFibonacciTarget() { - SuspectedTypo? typo = TargetPatterns.Find(new[] { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 114, 233, 377, 610, 987 }); + SuspectedTypo? typo = TargetPatterns.Find(Sequences.FibonacciWithTypo.Take(16).ToArray()); Assert.NotNull(typo); Assert.Equal((11, 114, 144), (typo!.Index, typo.Found, typo.Expected)); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/BenchmarkCommandTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/BenchmarkCommandTests.cs new file mode 100644 index 0000000..7d8bbdd --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/BenchmarkCommandTests.cs @@ -0,0 +1,20 @@ +using SnpEvolution.Search; + +namespace SnpEvolution.Tests.Cli +{ + [Collection(ProcessStateCollection.Name)] + public class BenchmarkCommandTests + { + [Fact] + public void ABenchmarkOfCompositionSearchBuildsFromThePartLibrary() + { + using var run = new CommandRun(); + run.CopyFolder("parts"); + + string output = run.Run("benchmark", "--algorithm", SearchCatalog.CompositionMapElites.Name, "--task", "Compute n", "--seeds", "1", "--budget", "20", + "--population", "5", "--library", "parts"); + + Assert.Contains("Composition search builds from", output); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/CommandArgsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/CommandArgsTests.cs index 461abd2..e55a9c2 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/CommandArgsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/CommandArgsTests.cs @@ -14,13 +14,12 @@ private static string Problem(params string[] line) return error; } + private static CommandArgs Parsed(params string[] line) => + CommandArgs.Parse(CommandRegistry.Find(line[0])!, line, out string error) ?? throw new ArgumentException(error); + // The settings evolve would run with, from the menu's defaults and the options given. - private static Settings Evolving(params string[] options) - { - string[] line = new[] { "evolve", "--target", "1,2" }.Concat(options).ToArray(); - CommandArgs args = CommandArgs.Parse(CommandRegistry.Find("evolve")!, line, out string error) ?? throw new ArgumentException(error); - return TargetArgs.Settings(args, SettingOptions.Evolve)!; - } + private static Settings Evolving(params string[] options) => + TargetArgs.Settings(Parsed(new[] { "evolve", "--target", "1,2" }.Concat(options).ToArray()), SettingOptions.Evolve)!; [Fact] public void AnUnknownOptionIsRefusedByName() => @@ -108,8 +107,7 @@ public void ModuleFilesTurnModulesOn() [Fact] public void OptionsGivenOverrideTheAdvisorsSuggestions() { - string[] line = { "evolve", "--target", "1,2", "--advise", "on", "--population", "3" }; - CommandArgs args = CommandArgs.Parse(CommandRegistry.Find("evolve")!, line, out string error) ?? throw new ArgumentException(error); + CommandArgs args = Parsed("evolve", "--target", "1,2", "--advise", "on", "--population", "3"); Assert.Equal(3, EvolveCommand.Configured(args, _ => { })!.PopulationSize); } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/EvolvePartsCommandTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/EvolvePartsCommandTests.cs new file mode 100644 index 0000000..9adc477 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/EvolvePartsCommandTests.cs @@ -0,0 +1,26 @@ +using SnpEvolution.Cli; +using SnpEvolution.Model; +using SnpEvolution.Storage; + +namespace SnpEvolution.Tests.Cli +{ + [Collection(ProcessStateCollection.Name)] + public class EvolvePartsCommandTests + { + [Fact] + [Slow] + public void EvolvePartsUnderTheProfileSavesAProfilePart() + { + using var console = new ConsoleCapture(); + using var temp = new TempFolder("snp-export"); + string folder = temp.Path; + + int exit = CommandLine.Run(new[] { "evolve-parts", "--only", "delay 1", "--budget", "5000", "--profile", "hardware", "--library", folder }); + + Assert.Equal((int)ExitCode.Success, exit); + var part = PartLibraryFiles.Read(File.ReadAllText(Path.Combine(folder, "delay-1.json")), "delay-1.json"); + Assert.Empty(HardwareProfile.Problems(part.Part.Network)); + Assert.EndsWith("--profile hardware", part.Origin.Run); + } + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/ExportCommandsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/ExportCommandsTests.cs index 70b8d4b..7619063 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/ExportCommandsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/ExportCommandsTests.cs @@ -6,6 +6,8 @@ namespace SnpEvolution.Tests.Cli [Collection(ProcessStateCollection.Name)] public class ExportCommandsTests { + private static readonly string Delay2 = RepositoryFiles.PartFile("parts", "delay-2.json"); + [Fact] public void ExportVerilogWritesTheDesignTestbenchAndExpectedTrace() { @@ -13,9 +15,9 @@ public void ExportVerilogWritesTheDesignTestbenchAndExpectedTrace() using var temp = new TempFolder("snp-export"); string folder = temp.Path; - int exit = CommandLine.Run(new[] { "export-verilog", "--part", RepositoryFiles.PartFile("parts", "delay-2.json"), "--out", folder, "--check", "off" }); + int exit = CommandLine.Run(new[] { "export-verilog", "--part", Delay2, "--out", folder, "--check", "off" }); - Assert.Equal(0, exit); + Assert.Equal((int)ExitCode.Success, exit); Assert.All(new[] { "delay_2.v", "delay_2_tb.v", "delay_2_expected.txt" }, file => Assert.True(File.Exists(Path.Combine(folder, file)), file)); } @@ -26,9 +28,9 @@ public void ExportUppaalWritesTheModelAndItsQueries() using var temp = new TempFolder("snp-export"); string folder = temp.Path; - int exit = CommandLine.Run(new[] { "export-uppaal", "--part", RepositoryFiles.PartFile("parts", "delay-2.json"), "--out", folder, "--check", "off" }); + int exit = CommandLine.Run(new[] { "export-uppaal", "--part", Delay2, "--out", folder, "--check", "off" }); - Assert.Equal(0, exit); + Assert.Equal((int)ExitCode.Success, exit); Assert.All(new[] { "delay-2.xml", "delay-2.q" }, file => Assert.True(File.Exists(Path.Combine(folder, file)), file)); } @@ -39,29 +41,12 @@ public void ExportNirRefusesAPartOutsideTheProfile() using var temp = new TempFolder("snp-export"); string folder = temp.Path; + int exit = CommandLine.Run(new[] { "export-nir", "--part", Delay2, "--out", folder }); - int exit = CommandLine.Run(new[] { "export-nir", "--part", RepositoryFiles.PartFile("parts", "delay-2.json"), "--out", folder }); - - Assert.Equal(1, exit); + Assert.Equal((int)ExitCode.Usage, exit); Assert.False(Directory.Exists(folder)); } - [Fact] - [Slow] - public void EvolvePartsUnderTheProfileSavesAProfilePart() - { - using var console = new ConsoleCapture(); - using var temp = new TempFolder("snp-export"); - string folder = temp.Path; - - int exit = CommandLine.Run(new[] { "evolve-parts", "--only", "delay 1", "--budget", "5000", "--profile", "hardware", "--library", folder }); - - Assert.Equal(0, exit); - var part = SnpEvolution.Storage.PartLibraryFiles.Read(File.ReadAllText(Path.Combine(folder, "delay-1.json")), "delay-1.json"); - Assert.Empty(SnpEvolution.Model.HardwareProfile.Problems(part.Part.Network)); - Assert.EndsWith("--profile hardware", part.Origin.Run); - } - [Fact] public void ACheckThatDiffersFromOurEngineHasItsOwnExitCode() { @@ -76,7 +61,7 @@ public void ExportNeedsAPartOrANetwork() using var console = new ConsoleCapture(); using var temp = new TempFolder("snp-export"); - Assert.Equal(1, CommandLine.Run(new[] { "export-verilog", "--out", temp.Path })); + Assert.Equal((int)ExitCode.Usage, CommandLine.Run(new[] { "export-verilog", "--out", temp.Path })); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/ListCommandsTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/ListCommandsTests.cs index f87e83c..972b549 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/ListCommandsTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/ListCommandsTests.cs @@ -1,5 +1,4 @@ using SnpEvolution.Search.Benchmarking; -using SnpEvolution.Tests.Golden; namespace SnpEvolution.Tests.Cli { diff --git a/SNP_Evolution/SNP_Evolution.Tests/Cli/VerifyCommandTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Cli/VerifyCommandTests.cs index f876dfd..d62bff6 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Cli/VerifyCommandTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Cli/VerifyCommandTests.cs @@ -18,7 +18,7 @@ public void VerifyRecordsTheProvenBoundInEachPartFile() int exit = CommandLine.Run(new[] { "verify", "--library", folder, "--seconds", "10" }); - Assert.Equal(0, exit); + Assert.Equal((int)ExitCode.Success, exit); ProvenBound? proven = PartLibraryFiles.Load(folder).Parts.Single().Part!.Proven; Assert.True(proven?.AllInputs); } @@ -38,7 +38,7 @@ public void OnlyVerifiesThePartsWhoseContractNamesMatch() int exit = CommandLine.Run(new[] { "verify", "--library", folder, "--only", "DELAY", "--seconds", "10" }); - Assert.Equal(0, exit); + Assert.Equal((int)ExitCode.Success, exit); Dictionary proven = PartLibraryFiles.Load(folder).Parts.Select(module => module.Part!).ToDictionary(part => part.Contract.Name, part => part.Proven); Assert.NotNull(proven["delay 2"]); Assert.Null(proven["sequencer 2"]); @@ -51,7 +51,7 @@ public void VerifyExitsWithTwoAndRecordsTheCounterexample() using var temp = new TempFolder("snp-verify").Made(); string file = Path.Combine(temp.Path, "register.json"); Part broken = PartFixtures.RegisterFailingAtTwenty(); - File.WriteAllText(file, PartLibraryFiles.ToJson(PartFixtures.Measured(broken, new PartOrigin(0, "by hand", 0)))); + File.WriteAllText(file, PartLibraryFiles.ToJson(PartFixtures.Measured(broken, PartFixtures.ByHand))); int exit = CommandLine.Run(new[] { "verify", "--part", file, "--bound", "24" }); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RecurrenceCompilerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RecurrenceCompilerTests.cs index 81b3474..06f0d33 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RecurrenceCompilerTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RecurrenceCompilerTests.cs @@ -7,10 +7,12 @@ namespace SnpEvolution.Tests.Compilation { public class RecurrenceCompilerTests { + private const int FittedValues = 16; + [Fact] public void FitsFibonacciAsTheSumOfTheTwoValuesBefore() { - Recurrence? recurrence = Recurrence.Fit(Sequences.Fibonacci.Take(16).ToList()); + Recurrence? recurrence = Recurrence.Fit(Sequences.Fibonacci.Take(FittedValues).ToList()); Assert.NotNull(recurrence); Assert.Equal(new[] { 1, 1 }, recurrence!.Coefficients); @@ -27,7 +29,7 @@ public void FindsNoRecurrenceForASequenceWithoutOne() [Fact] public void CompiledFibonacciMakesExactlyTheGapsAndContinuesPastTheTarget() { - Recurrence recurrence = Recurrence.Fit(Sequences.Fibonacci.Take(16).ToList())!; + Recurrence recurrence = Recurrence.Fit(Sequences.Fibonacci.Take(FittedValues).ToList())!; Network network = RecurrenceCompiler.Compile(recurrence); FitnessResult result = SequenceEvaluator(Sequences.Fibonacci).Evaluate(network); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterMachineCompilerTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterMachineCompilerTests.cs index 8ab662e..4c56f7f 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterMachineCompilerTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterMachineCompilerTests.cs @@ -5,7 +5,7 @@ namespace SnpEvolution.Tests.Compilation { public class RegisterMachineCompilerTests { - // Outputs a, then (a, b) becomes (b, a + b): every Sequences.Fibonacci number, with r4 only there to make the choice. + // Outputs a, then (a, b) becomes (b, a + b): every Fibonacci number, with r4 only there to make the choice. private const string FibonacciProgram = @" 0: ADD r1 -> 1 1: ADD r2 -> 2 @@ -42,7 +42,9 @@ public void ACompiledFibonacciProgramGeneratesTheSameNumbersAsTheProgram() RegisterProgram program = RegisterProgram.Parse(FibonacciProgram); (IReadOnlyList outputs, _, int steps) = program.Generate(outputLimit: 21, valueLimit: 100); - IReadOnlyList generated = Generated(RegisterMachineCompiler.Compile(program), 4 * (steps + 2) + 31); + // A generous bound on the network steps the compiled program needs to output 21 and count down its halt. + int networkSteps = 4 * (steps + 2) + 31; + IReadOnlyList generated = Generated(RegisterMachineCompiler.Compile(program), networkSteps); Assert.Equal(new[] { 1, 2, 3, 5, 8, 13, 21 }, outputs); Assert.Equal(outputs, generated.Where(number => number <= 21)); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs index 6354dc6..b8bcd9d 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Compilation/RegisterProgramTests.cs @@ -4,6 +4,8 @@ namespace SnpEvolution.Tests.Compilation { public class RegisterProgramTests { + private const string EvensProgram = "ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT"; + [Fact] public void ProgramsReadAndWriteTheSameWay() { @@ -16,7 +18,7 @@ public void ProgramsReadAndWriteTheSameWay() [Fact] public void ProgramsGenerateEveryNumberAlongEveryChoice() { - RegisterProgram evens = RegisterProgram.Parse("ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT"); + RegisterProgram evens = RegisterProgram.Parse(EvensProgram); (IReadOnlyList outputs, bool complete, _) = evens.Generate(maxSteps: 20); @@ -35,7 +37,7 @@ public void TheOutputRegisterCannotBeSubtractedFrom() [Fact] public void TheOutputLimitLosesNothingBelowIt() { - RegisterProgram evens = RegisterProgram.Parse("ADD r0 -> 1\nADD r0 -> 0 | 2\nHALT"); + RegisterProgram evens = RegisterProgram.Parse(EvensProgram); (IReadOnlyList outputs, bool complete, _) = evens.Generate(outputLimit: 7); diff --git a/SNP_Evolution/SNP_Evolution.Tests/Export/NirExporterTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Export/NirExporterTests.cs index 11b3842..eef35e9 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Export/NirExporterTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Export/NirExporterTests.cs @@ -12,35 +12,35 @@ namespace SnpEvolution.Tests.Export public class NirExporterTests { // Parts evolve-parts --profile hardware found, kept in the repository next to parts/. - private static Part ProfilePart(string file) - { - return RepositoryFiles.ReadPart("parts-profile", file).Part; - } + private const string ProfileFolder = "parts-profile"; + + private static readonly string[] ProfilePartFiles = { "delay-2.json", "sequencer-2.json" }; + + public static TheoryData ProfileFiles => new TheoryData(ProfilePartFiles); private static string Check(NirDescription description) { using var temp = new TempFolder("snp-nir"); - string folder = temp.Path; - return NirExporter.WriteAndCheck(description, folder); + return NirExporter.WriteAndCheck(description, temp.Path); } - [Fact] - public void TheProfilePartsMeetTheirContractsAndFitTheProfile() + [Theory] + [MemberData(nameof(ProfileFiles))] + public void TheProfilePartsMeetTheirContractsAndFitTheProfile(string file) { - foreach (string file in new[] { "delay-2.json", "sequencer-2.json" }) - { - Part part = ProfilePart(file); - Assert.True(Verifier.Measure(part, new EvaluationBudget()).Verdict is Verdict.Passed, file); - Assert.Empty(HardwareProfile.Problems(part.Network)); - } + Part part = RepositoryFiles.Part(ProfileFolder, file); + + Assert.True(Verifier.Measure(part, new EvaluationBudget()).Verdict is Verdict.Passed, file); + Assert.Empty(HardwareProfile.Problems(part.Network)); } + // Stays a loop over the files, since the attribute that skips it without the NIR tools is a fact. [NirFact] public void ProfilePartsLoadInNirAndMatchOurTracesInNorse() { - foreach (string file in new[] { "delay-2.json", "sequencer-2.json" }) + foreach (string file in ProfilePartFiles) { - string report = Check(NirExporter.Export(ProfilePart(file))); + string report = Check(NirExporter.Export(RepositoryFiles.Part(ProfileFolder, file))); Assert.Contains("matches SN P on all 1 case(s)", report); } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Export/UppaalExporterTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Export/UppaalExporterTests.cs index a691b98..825e050 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Export/UppaalExporterTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Export/UppaalExporterTests.cs @@ -9,15 +9,18 @@ namespace SnpEvolution.Tests.Export { public class UppaalExporterTests { - private static Part LibraryPart(string folder, string file) => RepositoryFiles.ReadPart(folder, file).Part; + private const string TraceQueryName = "every neuron holds what our engine's run holds, on every step"; + private const string DoneOnceQueryName = "done once, the right one, with the right outputs"; private static IReadOnlyList Check(Part part) { using var temp = new TempFolder("snp-uppaal"); - string folder = temp.Path; - return Verifyta.Check(UppaalExporter.Export(part), folder); + return Verifyta.Check(UppaalExporter.Export(part), temp.Path); } + private static IReadOnlyList CasesUpTo(Contract contract, int largest) => + Enumerable.Range(0, largest + 1).Select(n => Specification.For(contract)!.Expected(new Dictionary { ["n"] = n })).ToList(); + private static bool Holds(Part part, string query) { UppaalModel model = UppaalExporter.Export(part); @@ -27,14 +30,14 @@ private static bool Holds(Part part, string query) [Fact] public void ExportsWellFormedXmlWithAQueryPerContractRule() { - UppaalModel model = UppaalExporter.Export(LibraryPart("parts", "delay-2.json")); + UppaalModel model = UppaalExporter.Export(RepositoryFiles.Part("parts", "delay-2.json")); using var reader = XmlReader.Create(new StringReader(model.Model), new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }); while (reader.Read()) { } Assert.Equal("delay-2", model.Name); - Assert.Equal(UppaalExporter.ContractQueries.Select(query => query.Name).Append("every neuron holds what our engine's run holds, on every step"), model.QueryNames); + Assert.Equal(UppaalExporter.ContractQueries.Select(query => query.Name).Append(TraceQueryName), model.QueryNames); Assert.Contains(UppaalExporter.TraceQuery, model.Queries); Assert.Contains("system Clock, N1, N2;", model.Model); } @@ -42,7 +45,7 @@ public void ExportsWellFormedXmlWithAQueryPerContractRule() [Fact] public void TheRegisterModelStartsAndReadsEachCaseAsTheEngineDoes() { - Part register = HandBuiltParts.All().Single(part => part.Contract.Name == "register"); + Part register = PartFixtures.HandBuiltRegister(); var task = new ContractTask(register.Contract, register.Binding); string model = UppaalExporter.Export(register).Model; @@ -86,14 +89,14 @@ public void RefusesALegacyRuleWithADelay() [VerifytaFact] public void TheEvolvedDelayMeetsEveryContractRuleInUppaal() { - Assert.All(Check(LibraryPart("parts", "delay-2.json")), Assert.True); + Assert.All(Check(RepositoryFiles.Part("parts", "delay-2.json")), Assert.True); } [VerifytaFact] public void TheEvolvedSequencerAndProfileDelayMeetEveryContractRuleInUppaal() { - Assert.All(Check(LibraryPart("parts", "sequencer-2.json")), Assert.True); - Assert.All(Check(LibraryPart("parts-profile", "delay-2.json")), Assert.True); + Assert.All(Check(RepositoryFiles.Part("parts", "sequencer-2.json")), Assert.True); + Assert.All(Check(RepositoryFiles.Part("parts-profile", "delay-2.json")), Assert.True); } [VerifytaFact] @@ -101,23 +104,23 @@ public void UppaalFindsADelayThatFiresDoneTwice() { Part twice = PartFixtures.DelayFiringDoneTwice(2); - Assert.False(Holds(twice, "done once, the right one, with the right outputs")); - Assert.True(Holds(twice, "every neuron holds what our engine's run holds, on every step")); + Assert.False(Holds(twice, DoneOnceQueryName)); + Assert.True(Holds(twice, TraceQueryName)); } [VerifytaFact] public void TheHandBuiltRegisterMeetsEveryContractRuleOnEveryCaseInUppaal() { - Assert.All(Check(HandBuiltParts.All().Single(part => part.Contract.Name == "register")), Assert.True); + Assert.All(Check(PartFixtures.HandBuiltRegister()), Assert.True); } [VerifytaFact] public void UppaalAgreesWithTheBoundedCheckOnARegisterFailingAtTwenty() { Part broken = PartFixtures.RegisterFailingAtTwenty(); - Contract upToTwenty = broken.Contract with { Cases = Enumerable.Range(0, 21).Select(n => Specification.For(broken.Contract)!.Expected(new Dictionary { ["n"] = n })).ToList(), MaxLatency = Specifications.LatencyFor(20) }; + Contract upToTwenty = broken.Contract with { Cases = CasesUpTo(broken.Contract, 20), MaxLatency = Specifications.LatencyFor(20) }; - Assert.False(Holds(broken with { Contract = upToTwenty }, "done once, the right one, with the right outputs")); + Assert.False(Holds(broken with { Contract = upToTwenty }, DoneOnceQueryName)); } } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Export/VerilogExporterTests.cs b/SNP_Evolution/SNP_Evolution.Tests/Export/VerilogExporterTests.cs index 4307c26..e377b1e 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Export/VerilogExporterTests.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Export/VerilogExporterTests.cs @@ -11,8 +11,9 @@ namespace SnpEvolution.Tests.Export { public class VerilogExporterTests { - private static LibraryPart LibraryDelay() => - RepositoryFiles.ReadPart("parts", "delay-2.json"); + private const int RandomNetworksToExport = 25; + private const int MostRandomNetworksTried = 2000; + private const int RandomInputSteps = 40; // Runs every case of the part under iverilog and returns what it printed and what our engine says it should. private static (string Simulated, string Expected) CoSimulate(Part part) @@ -22,15 +23,14 @@ private static (string Simulated, string Expected) CoSimulate(Part part) List inputs = cases.Select(@case => @case.Input).ToList(); List steps = cases.Select(@case => @case.Steps).ToList(); using var temp = new TempFolder("snp-verilog"); - string folder = temp.Path; - return (Iverilog.Simulate(design, VerilogTestbench.For(design, inputs, steps), folder), VerilogTestbench.ExpectedOutput(part.Network, inputs, steps)); + return (Iverilog.Simulate(design, VerilogTestbench.For(design, inputs, steps), temp.Path), VerilogTestbench.ExpectedOutput(part.Network, inputs, steps)); } public static TheoryData Parts() => new TheoryData { "library delay 2", "register", "add", "increment" }; private static Part Named(string name) => name switch { - "library delay 2" => LibraryDelay().Part, + "library delay 2" => RepositoryFiles.Part("parts", "delay-2.json"), "register" => PartFixtures.Register(), "add" => ReferenceParts.Add(), _ => ReferenceParts.Increment(), @@ -66,7 +66,7 @@ public void RandomDeterministicNetworksMatchOurEngineStepForStep() var input = new InputSpikes(new IReadOnlyList[] { new[] { 0, 1, 1, 4, 9 }, new[] { 2, 3, 7 } }); int exported = 0; var delays = new HashSet(); - for (int attempt = 0; exported < 25 && attempt < 2000; attempt++) + for (int attempt = 0; exported < RandomNetworksToExport && attempt < MostRandomNetworksTried; attempt++) { Network network = random.Next(3) == 0 ? AxonalCopy(factory.NewNetwork(), random) : factory.NewNetwork(); if (SpikeTrace.Choices(network).Count > 0) @@ -75,15 +75,14 @@ public void RandomDeterministicNetworksMatchOurEngineStepForStep() } exported++; delays.UnionWith(network.Neurons.SelectMany(neuron => neuron.Rules).Where(rule => rule.Delay > 0).Select(rule => rule.DelayKind)); - long mostHeld = SpikeTrace.Run(network, input, 40).MostHeld; + long mostHeld = SpikeTrace.Run(network, input, RandomInputSteps).MostHeld; VerilogDesign design = VerilogExporter.Export(network, $"random {attempt}", NetworkPort.Plain(network), mostHeld); using var temp = new TempFolder("snp-verilog"); - string folder = temp.Path; - string simulated = Iverilog.Simulate(design, VerilogTestbench.For(design, new[] { input }, new[] { 40 }), folder); - string expected = VerilogTestbench.ExpectedOutput(network, new[] { input }, new[] { 40 }); + string simulated = Iverilog.Simulate(design, VerilogTestbench.For(design, new[] { input }, new[] { RandomInputSteps }), temp.Path); + string expected = VerilogTestbench.ExpectedOutput(network, new[] { input }, new[] { RandomInputSteps }); Assert.True(expected == simulated, $"{NetworkNotation.Format(network)}\ndiffers from our engine. {TextDifference.FirstDifference(expected, simulated)}"); } - Assert.Equal(25, exported); + Assert.Equal(RandomNetworksToExport, exported); Assert.Equal(new[] { DelayKind.Closing, DelayKind.Holding, DelayKind.Axonal }, delays.OrderBy(kind => kind)); } @@ -97,14 +96,6 @@ public void RefusesANondeterministicNetworkAndSaysWhichRulesCompete() Assert.Contains("Neuron 1 could apply rule 1 (a+/a -> a) or rule 2 (aa(a)*/aa -> a) when it holds 2 spike(s)", refusal.Message); } - [Fact] - public void FindsRulesThatOnlyCompeteOnceTheNeuronHoldsWhatTheyConsume() - { - var network = new Network(new[] { OutputNeuron(0, Standard("a+", 5), Standard("a+", 5, produce: 2)) }); - - Assert.Contains("when it holds 5 spike(s)", Assert.Single(SpikeTrace.Choices(network))); - } - [Fact] public void SizesCountersFromTheRegisterWidth() { @@ -128,6 +119,5 @@ public void ExportsTheSameTextEveryTime() private static Network AxonalCopy(Network network, Random random) => new Network(network.Neurons.Select(neuron => neuron.WithRules(neuron.Rules.Select(rule => rule.Delay > 0 && random.Next(2) == 0 ? rule.WithAxonal(true) : rule))).ToList()); - } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/BenchmarkFixtures.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/BenchmarkFixtures.cs index 5584788..222b4dd 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/BenchmarkFixtures.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/BenchmarkFixtures.cs @@ -1,4 +1,5 @@ using SnpEvolution.Search.Benchmarking; +using SnpEvolution.Specs.Parts; namespace SnpEvolution.Tests.Fixtures { @@ -6,5 +7,7 @@ internal static class BenchmarkFixtures { // One seed per algorithm and task, so a benchmark test stays quick. public static readonly BenchmarkSettings OneSeed = BenchmarkSettings.Default with { Seeds = 1 }; + + public static BenchmarkSettings WithParts(ModuleLibrary library) => OneSeed with { Parts = library.Parts.Select(module => module.Part!).ToList() }; } } diff --git a/SNP_Evolution/SNP_Evolution.Tests/Fixtures/CatalogEntries.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/CatalogEntries.cs new file mode 100644 index 0000000..b5162a1 --- /dev/null +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/CatalogEntries.cs @@ -0,0 +1,11 @@ +using SnpEvolution.Application; +using SnpEvolution.Simulation; + +namespace SnpEvolution.Tests.Fixtures +{ + internal static class CatalogEntries + { + // One thread keeps a run's evaluation order, and so its counts, the same on every machine. + public static CatalogEntry SingleThreadEngine => Catalog.Engines.Single(engine => engine.Name == "CPU, single thread"); + } +} diff --git a/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandRun.cs b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/CommandRun.cs similarity index 95% rename from SNP_Evolution/SNP_Evolution.Tests/Golden/CommandRun.cs rename to SNP_Evolution/SNP_Evolution.Tests/Fixtures/CommandRun.cs index af3271e..497a27b 100644 --- a/SNP_Evolution/SNP_Evolution.Tests/Golden/CommandRun.cs +++ b/SNP_Evolution/SNP_Evolution.Tests/Fixtures/CommandRun.cs @@ -5,7 +5,7 @@ using SnpEvolution.Specs.Parts; using SnpEvolution.Storage; -namespace SnpEvolution.Tests.Golden +namespace SnpEvolution.Tests.Fixtures { // Swaps the process-wide console, working directory and culture, so tests using it belong to ProcessStateCollection. internal sealed partial class CommandRun : IDisposable @@ -91,9 +91,9 @@ private void AppendFiles(StringBuilder text) private string Normalise(string text) { // On macOS the temporary folder is reached through /private as well. - foreach (string folder in new[] { Path.Combine("/private", Folder.TrimStart('/')), Folder }) + foreach (string spelling in new[] { Path.Combine("/private", Folder.TrimStart('/')), Folder }) { - text = text.Replace(folder, ""); + text = text.Replace(spelling, ""); } text = RunFolder().Replace(text, "runs/"); text = Seconds().Replace(text, "in