Table of Contents

Namespace OptunaSharp.Extensions

Classes

OptunaServiceCollectionExtensions

Microsoft.Extensions.DependencyInjection wire-up for OptunaSharp's core building blocks: register the app-wide BaseSampler, BasePruner and BaseStorage once, then resolve an IOptunaStudyFactory that composes them into studies. This is a .NET-ergonomics layer over the functional CreateStudy(BaseStorage?, BaseSampler?, BasePruner?, string?, StudyDirection?, bool, IEnumerable<StudyDirection>?) API — it changes nothing about how a study behaves, only where it is configured.

The registrations compose with the satellite packages: e.g. services.AddSqliteStorage("sqlite:///study.db").AddBoTorchSampler().AddOptunaStudyFactory() wires storage + sampler + factory across three assemblies with no glue code.

Lifetimes. Samplers and pruners hold per-study state (RNG streams, generation trackers, search-space caches), so their factory registrations default to Transient: every study the factory builds gets its own instance, as every Python create_study() call builds its own default sampler. Storages are shared infrastructure and default to Singleton. An instance registration is always a singleton, shared by every study.

OptunaStudyFactory

The default IOptunaStudyFactory: resolves the optional BaseSampler / BasePruner / BaseStorage from the service provider at each call and delegates to CreateStudy(BaseStorage?, BaseSampler?, BasePruner?, string?, StudyDirection?, bool, IEnumerable<StudyDirection>?). Registered by AddOptunaStudyFactory(IServiceCollection, Action<OptunaStudyOptions>?); construct it directly only when composing a container by hand.

OptunaStudyOptions

The container-bound defaults an IOptunaStudyFactory applies when a call to CreateStudy(string?, StudyDirection?, bool?, IEnumerable<StudyDirection>?) leaves an argument unspecified. Configure it from AddOptunaStudyFactory(configure) so a whole application shares one study shape (direction, naming, conflict handling) while the sampler / pruner / storage come from the other AddOptuna* registrations.

Consequence: a factory reads these only as fall-backs — an explicit argument to CreateStudy(string?, StudyDirection?, bool?, IEnumerable<StudyDirection>?) always wins. Leaving DefaultDirection and DefaultDirections both null reproduces Optuna's own default (a single-objective minimize study).

Interfaces

IOptunaStudyFactory

A dependency-injection composition seam that builds Study instances from the sampler / pruner / storage registered in the container, applying the container-bound OptunaStudyOptions defaults. Resolve it (constructor-inject IOptunaStudyFactory) instead of calling CreateStudy(BaseStorage?, BaseSampler?, BasePruner?, string?, StudyDirection?, bool, IEnumerable<StudyDirection>?) directly, so a study's algorithm and persistence are configured once at the composition root rather than at every call site.

Why an interface (and why a factory, not a singleton Study): a study is a per-run object — its name and direction vary, and a persistent storage may already hold one — so it cannot be a container singleton. Each call mints (or re-attaches to) a study. The interface makes the seam mockable in tests.

Lifetime: AddOptunaStudyFactory registers the factory as transient, so it resolves its sampler, pruner and storage from the provider that resolved it: a request scope resolves scoped registrations from that scope, and the root provider (a singleton or hosted service) resolves singletons and transients. The sampler and pruner factory registrations default to transient, so each study gets its own sampler and pruner unless one instance was registered on purpose.