private Individual[] TryMutateWithParallelOptions(Array <Individual> population, ParallelOptions options)
        {
            var failedAttempts = 0L;
            var mutants        = new Individual[_mutantsPerGeneration];

            Parallel.For(
                fromInclusive: 0,
                toExclusive: mutants.Length,
                parallelOptions: options,
                (long index, ParallelLoopState loopState) => {
                while (true)
                {
                    var updatedFailedAttempts = Interlocked.Read(ref failedAttempts);
                    if (updatedFailedAttempts >= _maximumFailedAttemptsPerGeneration)
                    {
                        loopState.Stop();
                    }

                    var mutationCandidate = Random.Choice(population);

                    var mutated = _individualMutator.TryMutate(original: mutationCandidate);
                    if (mutated is null)
                    {
                        updatedFailedAttempts = Interlocked.Increment(ref failedAttempts);
                    }
                    else
                    {
                        mutants[index] = mutated;
                        break;
                    }
                }
            });

            return(mutants);
        }
        public Rule?TryCreateRule(ReadOnlySpan <Rule> existingRules)
        {
            var seedsIndices = _seedSelector.FindSeedsIndices(existingRules);

            if (seedsIndices.IsEmpty)
            {
                return(null);
            }

            var seedIndex = Random.Choice(seedsIndices);
            var seed      = _dataset.GetInstanceData(seedIndex);

            var boxes = _boxConverter.FromRules(existingRules);

            if (_runExpensiveSanityChecks)
            {
                for (int i = 0; i < boxes.Length; i++)
                {
                    var box = boxes[i];
                    if (box.Contains(seed))
                    {
                        throw new InvalidOperationException();
                    }
                }
            }

            var dimensionExpansionOrder = NaturalRange.CreateShuffled(
                inclusiveStart: 0,
                exclusiveEnd: _dataset.FeatureCount);

            var secureRectangle = _boxCreator.TryCreateLargestNonIntersectingRectangle(
                seedIndex: seedIndex,
                existingHyperRectangles: boxes,
                dimensionExpansionOrder: dimensionExpansionOrder);

            if (secureRectangle is null)
            {
                return(null);
            }

            if (_runExpensiveSanityChecks)
            {
                for (int i = 0; i < boxes.Length; i++)
                {
                    var box = boxes[i];
                    if (_rectangleIntersector.IntersectsInAllDimension(secureRectangle, box))
                    {
                        throw new InvalidOperationException();
                    }
                }
            }

            var secureRectangleCoverage = _coverageComputer.ComputeCoverage(secureRectangle);
            var coveredInstancesIndices = secureRectangleCoverage.IndicesOfCoveredInstances.ToArray();

            if (coveredInstancesIndices.Length == 0)
            {
                return(null);
            }

            var coveredInstancesDistancesToSeed = _dataset.ComputeDistances(
                targetInstanceIndex: seedIndex,
                otherInstancesIndices: coveredInstancesIndices);

            Array.Sort(
                keys: coveredInstancesDistancesToSeed,
                items: coveredInstancesIndices);

            var instancesToCover  = Math.Min(_targetNumberOfInstancesToCover, coveredInstancesIndices.Length);
            var relevantInstances = coveredInstancesIndices
                                    .AsSpan()
                                    .Slice(start: 0, length: instancesToCover);

            var ruleAntecedent = _antecedentCreator.CreateAntecedent(
                seedIndex: seedIndex,
                nearestInstancesIndices: relevantInstances);

            if (ruleAntecedent is null)
            {
                return(null);
            }

            var ruleConsequent = _consequentCreator.CreateConsequent(relevantInstances);

            return(new Rule(
                       antecedent: ruleAntecedent,
                       consequent: ruleConsequent));
        }