diff --git a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/BracKtApi.kt b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/BracKtApi.kt index bc4aed8..b15ec05 100644 --- a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/BracKtApi.kt +++ b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/BracKtApi.kt @@ -1,17 +1,23 @@ +@file:OptIn(ExperimentalJsExport::class) + package net.javaman.brackt.api +import net.javaman.brackt.api.util.injections.InjectionAdder import net.javaman.brackt.api.util.injections.InjectionManager import net.javaman.brackt.api.util.logging.Logger import net.javaman.brackt.api.util.properties.PropertyManager +import kotlin.js.ExperimentalJsExport +import kotlin.js.JsExport import kotlin.jvm.JvmStatic +@JsExport class BracKtApi private constructor() { - companion object { + companion object : InjectionAdder { /** * Add injections for this module */ @JvmStatic - fun addInjections() = InjectionManager.add { + override fun addInjections() = InjectionManager.add { many { Logger(it) } one { PropertyManager() } } diff --git a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/quantum/QuantumGate.kt b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/quantum/QuantumGate.kt index 28e5a98..89a148a 100644 --- a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/quantum/QuantumGate.kt +++ b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/quantum/QuantumGate.kt @@ -6,18 +6,18 @@ package net.javaman.brackt.api.quantum * Custom providers can implement this interface and write extensions for [QuantumCircuit] */ interface QuantumGate { - data class Identity(val qubit: Int) : QuantumGate - data class PauliX(val qubit: Int) : QuantumGate - data class PauliY(val qubit: Int) : QuantumGate - data class PauliZ(val qubit: Int) : QuantumGate - data class Hadamard(val qubit: Int) : QuantumGate - data class S(val qubit: Int) : QuantumGate - data class SDagger(val qubit: Int) : QuantumGate - data class T(val qubit: Int) : QuantumGate - data class TDagger(val qubit: Int) : QuantumGate - data class Phase(val qubit: Int, val phi: Double) : QuantumGate - data class U(val qubit: Int, val theta: Double, val phi: Double, val lambda: Double) : QuantumGate - data class ControlledX(val controlQubit: Int, val targetQubit: Int) : QuantumGate - data class ControlledZ(val qubit1: Int, val qubit2: Int) : QuantumGate - data class Measure(val qubit: Int, val bit: Int) : QuantumGate + class Identity(val qubit: Int) : QuantumGate + class PauliX(val qubit: Int) : QuantumGate + class PauliY(val qubit: Int) : QuantumGate + class PauliZ(val qubit: Int) : QuantumGate + class Hadamard(val qubit: Int) : QuantumGate + class S(val qubit: Int) : QuantumGate + class SDagger(val qubit: Int) : QuantumGate + class T(val qubit: Int) : QuantumGate + class TDagger(val qubit: Int) : QuantumGate + class Phase(val qubit: Int, val phi: Double) : QuantumGate + class U(val qubit: Int, val theta: Double, val phi: Double, val lambda: Double) : QuantumGate + class ControlledX(val controlQubit: Int, val targetQubit: Int) : QuantumGate + class ControlledZ(val qubit1: Int, val qubit2: Int) : QuantumGate + class Measure(val qubit: Int, val bit: Int) : QuantumGate } diff --git a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/injections/InjectionManager.kt b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/injections/InjectionManager.kt index 2b5d7a4..1e5c709 100644 --- a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/injections/InjectionManager.kt +++ b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/injections/InjectionManager.kt @@ -1,6 +1,7 @@ package net.javaman.brackt.api.util.injections import net.javaman.brackt.api.util.logging.Logger +import net.javaman.brackt.api.util.reflection.getPlatformName import kotlin.jvm.JvmStatic import kotlin.reflect.KClass import kotlin.reflect.KProperty @@ -37,14 +38,16 @@ class InjectionManager { } inline fun many(noinline block: (KClass<*>) -> T) { - if (manyInstanceMap.keys.none { it == T::class }) manyInstanceMap[T::class] = block + if (manyInstanceMap.keys.none { it == T::class}) { + manyInstanceMap[T::class] = block + } } inline operator fun getValue(thisRef: Any, property: KProperty<*>): T { return manyInstanceMap.keys.firstOrNull { it == T::class }?.let { manyInstanceMap[it]!!.invoke(thisRef::class) as T } ?: oneInstanceList.firstOrNull { T::class.isInstance(it) } as? T - ?: throw UninitializedInjectionException("") + ?: throw UninitializedInjectionException("${T::class.getPlatformName()} has not yet been initialized") } } @@ -54,3 +57,7 @@ class InjectionManager { fun injection() = InjectionManager() class UninitializedInjectionException(override val message: String) : Exception(message) + +interface InjectionAdder { + fun addInjections() +} diff --git a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/logging/Logger.kt b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/logging/Logger.kt index 364d318..3e2499f 100644 --- a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/logging/Logger.kt +++ b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/logging/Logger.kt @@ -6,7 +6,7 @@ import kotlinx.datetime.Clock import net.javaman.brackt.api.util.formatters.PadDirection import net.javaman.brackt.api.util.formatters.pretty import net.javaman.brackt.api.util.formatters.withLength -import net.javaman.brackt.api.util.reflection.getPlatformQualifiedName +import net.javaman.brackt.api.util.reflection.getPlatformName import kotlin.js.ExperimentalJsExport import kotlin.js.JsExport import kotlin.js.JsName @@ -28,7 +28,7 @@ class Logger(val className: String) { val LEVEL_CHARS = LoggingLevel.values().maxOf { it.name.length } } - @JsName("LoggerByClass") constructor(kClass: KClass<*>) : this(kClass.getPlatformQualifiedName()) + @JsName("LoggerByClass") constructor(kClass: KClass<*>) : this(kClass.getPlatformName()) fun log(level: LoggingLevel, block: () -> Any) { if (acceptableLevel.severity <= level.severity) { diff --git a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt index 4b245a4..aafffc2 100644 --- a/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt +++ b/brac-kt-api/src/commonMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt @@ -2,4 +2,4 @@ package net.javaman.brackt.api.util.reflection import kotlin.reflect.KClass -expect fun KClass<*>.getPlatformQualifiedName(): String +expect fun KClass<*>.getPlatformName(): String diff --git a/brac-kt-api/src/jsMain/kotlin/net/javaman/brackt/api/BracKtApi.kt b/brac-kt-api/src/jsMain/kotlin/net/javaman/brackt/api/BracKtApi.kt new file mode 100644 index 0000000..567e07f --- /dev/null +++ b/brac-kt-api/src/jsMain/kotlin/net/javaman/brackt/api/BracKtApi.kt @@ -0,0 +1,7 @@ +@file:OptIn(ExperimentalJsExport::class) + +package net.javaman.brackt.api + +// Due to a Kotlin/JS bug when compiling companion objects, we must redeclare this static method +@JsExport +fun addInjections() = BracKtApi.addInjections() diff --git a/brac-kt-api/src/jsMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt b/brac-kt-api/src/jsMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt index 5a7aa41..de7c24e 100644 --- a/brac-kt-api/src/jsMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt +++ b/brac-kt-api/src/jsMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt @@ -5,4 +5,4 @@ import kotlin.reflect.KClass /** * Qualified name reflection is not supported by Kotlin/JS */ -actual fun KClass<*>.getPlatformQualifiedName() = "brac-kt" +actual fun KClass<*>.getPlatformName() = this.simpleName ?: "Anonymous" diff --git a/brac-kt-api/src/jvmMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt b/brac-kt-api/src/jvmMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt index bcd744b..9166734 100644 --- a/brac-kt-api/src/jvmMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt +++ b/brac-kt-api/src/jvmMain/kotlin/net/javaman/brackt/api/util/reflection/Reflection.kt @@ -2,4 +2,4 @@ package net.javaman.brackt.api.util.reflection import kotlin.reflect.KClass -actual fun KClass<*>.getPlatformQualifiedName() = this.qualifiedName ?: "Anonymous" +actual fun KClass<*>.getPlatformName() = this.qualifiedName ?: "Anonymous" diff --git a/brac-kt-ibmq-provider/build.gradle.kts b/brac-kt-ibmq-provider/build.gradle.kts index d42781b..5a4f7bf 100644 --- a/brac-kt-ibmq-provider/build.gradle.kts +++ b/brac-kt-ibmq-provider/build.gradle.kts @@ -3,6 +3,7 @@ plugins { kotlin("plugin.serialization") id("org.jetbrains.dokka") id("maven-publish") + id("io.gitlab.arturbosch.detekt") } repositories { diff --git a/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt b/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt deleted file mode 100644 index 07b5d8f..0000000 --- a/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt +++ /dev/null @@ -1,329 +0,0 @@ -@file:OptIn(ExperimentalJsExport::class) - -package net.javaman.brackt.providers.ibmq - -import io.ktor.client.request.get -import kotlinx.coroutines.delay -import kotlinx.datetime.Clock -import net.javaman.brackt.api.quantum.QuantumCircuit -import net.javaman.brackt.api.util.formatters.decodedUrl -import net.javaman.brackt.api.util.injections.InjectionManager -import net.javaman.brackt.api.util.injections.injection -import net.javaman.brackt.api.util.logging.Logger -import net.javaman.brackt.providers.ibmq.api.IbmqApi -import net.javaman.brackt.providers.ibmq.api.apiToken -import net.javaman.brackt.providers.ibmq.api.backends -import net.javaman.brackt.providers.ibmq.api.client -import net.javaman.brackt.providers.ibmq.api.job -import net.javaman.brackt.providers.ibmq.api.jobs -import net.javaman.brackt.providers.ibmq.api.jobsLimit -import net.javaman.brackt.providers.ibmq.api.lastest -import net.javaman.brackt.providers.ibmq.api.latest -import net.javaman.brackt.providers.ibmq.api.logInWithToken -import net.javaman.brackt.providers.ibmq.api.models.BackendResponse -import net.javaman.brackt.providers.ibmq.api.models.BackendsResponse -import net.javaman.brackt.providers.ibmq.api.models.Code -import net.javaman.brackt.providers.ibmq.api.models.LatestResponse -import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenRequest -import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenResponse -import net.javaman.brackt.providers.ibmq.api.models.NetworkGroup -import net.javaman.brackt.providers.ibmq.api.models.NetworkHub -import net.javaman.brackt.providers.ibmq.api.models.NetworkProject -import net.javaman.brackt.providers.ibmq.api.models.NetworksResponse -import net.javaman.brackt.providers.ibmq.api.models.NewRequest -import net.javaman.brackt.providers.ibmq.api.models.NewResponse -import net.javaman.brackt.providers.ibmq.api.models.ResultResponse -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentBackend -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentQasm -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentResponse -import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest -import net.javaman.brackt.providers.ibmq.api.models.VersionsResponse -import net.javaman.brackt.providers.ibmq.api.networks -import net.javaman.brackt.providers.ibmq.api.newCode -import net.javaman.brackt.providers.ibmq.api.resultDownloadUrl -import net.javaman.brackt.providers.ibmq.api.runExperiment -import net.javaman.brackt.providers.ibmq.api.versions -import net.javaman.brackt.providers.ibmq.transpiler.toQasm -import kotlin.js.ExperimentalJsExport -import kotlin.js.JsExport -import kotlin.js.JsName -import kotlin.jvm.JvmStatic -import kotlin.time.Duration -import kotlin.time.Duration.Companion.minutes -import kotlin.time.Duration.Companion.seconds -import kotlin.time.ExperimentalTime - -/** - * An interface for [IbmqApi] that saves session data - */ -@Suppress("TooManyFunctions") // All are needed -@JsExport -class IbmqProvider { - val ibmqApi: IbmqApi by injection() - val logger: Logger by injection() - - lateinit var accessToken: String - lateinit var userId: String - lateinit var network: NetworkHub - lateinit var group: NetworkGroup - lateinit var project: NetworkProject - lateinit var device: BackendResponse - lateinit var code: Code - lateinit var jobId: String - - companion object { - const val DEVICE_STATUS_ACTIVE = "active" - const val JOB_STATUS_COMPLETE = "COMPLETED" - - @OptIn(ExperimentalTime::class) - @JvmStatic - val JOB_TIMEOUT_DURATION = 5.minutes - - @OptIn(ExperimentalTime::class) - @JvmStatic - val JOB_POLL_INTERVAL = 2.seconds - - @OptIn(ExperimentalTime::class) - @JvmStatic - val JOB_LOG_INTERVAL = 10.seconds - - /** - * Add injections for this module - */ - @JvmStatic - fun addInjections() = InjectionManager.add { - one { IbmqApi() } - one { IbmqProvider() } - } - } -} - -/** - * Log in using an API token; for JVM, pass an environment variable from [PropertyManager] - */ -suspend fun IbmqProvider.logIn(apiToken: String): LogInWithTokenResponse { - val request = LogInWithTokenRequest(apiToken) - val response = ibmqApi.logInWithToken(request) - accessToken = response.id - userId = response.userId - return response -} - -/** - * Get the API token associated with this session - */ -suspend fun IbmqProvider.getApiToken() = ibmqApi.apiToken(accessToken, userId) - -/** - * Select the default network, including the default group and project - */ -suspend fun IbmqProvider.selectNetwork() = selectNetwork { networks -> - network = networks.first { it.isDefault } - val groupEntry = network.groups.entries.first { it.value.isDefault } - group = groupEntry.value - val projectEntry = groupEntry.value.projects.entries.first { it.value.isDefault } - project = projectEntry.value -} - -/** - * Select a network manually using a lambda - */ -@JsName("selectNetworkWithPicker") -suspend fun IbmqProvider.selectNetwork(picker: IbmqProvider.(NetworksResponse) -> Unit) { - val response = ibmqApi.networks(accessToken) - picker(this, response) - logger.info { "Selected network with name (${network.name})" } - logger.info { "Selected group with name (${group.name})" } - logger.info { "Selected project with name (${project.name})" } -} - -/** - * Select a device; default choice predicates: - * - Simulator - * - 5 or more qubits - * - Smallest queue - */ -suspend fun IbmqProvider.selectDevice(simulator: Boolean = true, minQubits: Int = 5) = selectDevice { devices -> - val projectDeviceNames = project.devices.values.map { it.name } - device = devices.filter { - projectDeviceNames.contains(it.name) && - it.isSimulator == simulator && - it.qubits >= minQubits && - it.deviceStatus.state && - it.deviceStatus.status == IbmqProvider.DEVICE_STATUS_ACTIVE - }.minByOrNull { - it.queueLength - } ?: throw DeviceNotAvailableException( - "Could not select a device because none matched the default predicates" - ) -} - -/** - * Select a device using a custom lambda - */ -@JsName("selectDeviceWithPicker") -suspend fun IbmqProvider.selectDevice(picker: IbmqProvider.(BackendsResponse) -> Unit) { - val response = ibmqApi.backends(accessToken) - picker(this, response) - logger.info { "Selected device with name (${device.name})" } -} - -/** - * Get the currently running jobs - */ -suspend fun IbmqProvider.getJobs() = ibmqApi.jobs(accessToken) - -/** - * Get the number of currently running and maximum number of jobs - */ -suspend fun IbmqProvider.getJobsLimit() = - ibmqApi.jobsLimit(accessToken, network.name, group.name, project.name, device.name) - -/** - * Run an experiment on a device - */ -@JsName("runExperimentWithRequest") -suspend fun IbmqProvider.runExperiment(request: RunExperimentRequest): RunExperimentResponse { - val response = ibmqApi.runExperiment( - accessToken, - request, - network.name, - group.name, - project.name - ) - jobId = response.id - return response -} - -/** - * Run a [QuantumCircuit] on a device - */ -suspend fun IbmqProvider.runExperiment(qc: QuantumCircuit, shots: Int = 1024, name: String = "") = runExperiment( - RunExperimentRequest( - qasms = listOf( - RunExperimentQasm( - qasm = qc.toQasm() - ) - ), - backend = RunExperimentBackend( - name = device.name - ), - codeId = "", - shots = shots, - name = name - ) -) - -/** - * Upload a new version to a saved circuit - */ -suspend fun IbmqProvider.updateExperiment(codeId: String, request: VersionsRequest): VersionsResponse { - val response = ibmqApi.versions(accessToken, request, codeId) - code = response - return response -} - -/** - * Get the latest version of a circuit - */ -suspend fun IbmqProvider.getExperiment(codeId: String): LatestResponse { - val response = ibmqApi.latest(accessToken, codeId) - code = response - return response -} - -/** - * Get all saved circuits - */ -suspend fun IbmqProvider.getExperiments() = ibmqApi.lastest(accessToken, userId) - -/** - * Save a new circuit - */ -suspend fun IbmqProvider.newExperiment(request: NewRequest): NewResponse { - val response = ibmqApi.newCode(accessToken, request) - code = response - return response -} - -/** - * Get the status of a run - */ -suspend fun IbmqProvider.getRun(jobId: String) = ibmqApi.job(accessToken, jobId) - -/** - * Get a URL for the results of a run - */ -suspend fun IbmqProvider.getResultDownloadUrl(jobId: String) = ibmqApi.resultDownloadUrl(accessToken, jobId) - -/** - * Get the results of a run from a URL - */ -suspend fun IbmqProvider.getResult(url: String): ResultResponse { - logger.info { "Getting results from a URL" } - val response = client.get(url) - logger.info { "Received results download with size (${response.results.size})" } - return response -} - -@OptIn(ExperimentalTime::class) -suspend fun IbmqProvider.andWait(timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION): ResultResponse { - val tStart = Clock.System.now() - var runResponse = getRun(jobId) - var tEnd = Clock.System.now() - var tLastLog = Clock.System.now() - while (runResponse.status != IbmqProvider.JOB_STATUS_COMPLETE) { - delay(IbmqProvider.JOB_POLL_INTERVAL) - runResponse = getRun(jobId) - tEnd = Clock.System.now() - if (tEnd.minus(tLastLog) > IbmqProvider.JOB_LOG_INTERVAL) { - logger.info { "Time elapsed since job was requested: (${tEnd.minus(tStart).inWholeSeconds}) seconds" } - tLastLog = tEnd - } - if (tEnd.minus(tStart) > timeoutDuration) { - throw JobTimeoutException( - "Job timed out after duration (${tEnd.minus(tStart).inWholeSeconds}) seconds " + - "seconds; the job is probably still active, but we're impatient!" - ) - } - } - logger.info { - "Total time elapsed from job request to response: (${tEnd.minus(tStart).inWholeSeconds}) seconds" - } - val resultDownloadUrl = getResultDownloadUrl(jobId).url.decodedUrl() - val result = getResult(resultDownloadUrl) - logger.info { result.results[0].data.pretty() } - return result -} - -/** - * Run an experiment and wait for the job to complete - */ -@OptIn(ExperimentalTime::class) -@JsName("runExperimentAndWaitWithRequest") -suspend fun IbmqProvider.runExperimentAndWait( - request: RunExperimentRequest, - timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION -): ResultResponse { - runExperiment(request) - return andWait(timeoutDuration) -} - -/** - * Run an experiment and wait for the job to complete - */ -@OptIn(ExperimentalTime::class) -suspend fun IbmqProvider.runExperimentAndWait( - qc: QuantumCircuit, - timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION -): ResultResponse { - runExperiment(qc) - return andWait(timeoutDuration) -} - -@JsExport -class DeviceNotAvailableException(message: String) : Exception(message) - -@JsExport -class JobTimeoutException(message: String) : Exception(message) diff --git a/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderImpl.kt b/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderImpl.kt new file mode 100644 index 0000000..7fff20d --- /dev/null +++ b/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderImpl.kt @@ -0,0 +1,332 @@ +@file:OptIn(ExperimentalJsExport::class) + +package net.javaman.brackt.providers.ibmq + +import io.ktor.client.request.get +import kotlinx.coroutines.delay +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import net.javaman.brackt.api.quantum.QuantumCircuit +import net.javaman.brackt.api.util.formatters.decodedUrl +import net.javaman.brackt.api.util.injections.InjectionAdder +import net.javaman.brackt.api.util.injections.InjectionManager +import net.javaman.brackt.api.util.injections.injection +import net.javaman.brackt.api.util.logging.Logger +import net.javaman.brackt.providers.ibmq.api.IbmqApi +import net.javaman.brackt.providers.ibmq.api.IbmqApiImpl +import net.javaman.brackt.providers.ibmq.api.client +import net.javaman.brackt.providers.ibmq.api.models.BackendResponse +import net.javaman.brackt.providers.ibmq.api.models.BackendsResponse +import net.javaman.brackt.providers.ibmq.api.models.Code +import net.javaman.brackt.providers.ibmq.api.models.LatestResponse +import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenRequest +import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenResponse +import net.javaman.brackt.providers.ibmq.api.models.NetworkGroup +import net.javaman.brackt.providers.ibmq.api.models.NetworkHub +import net.javaman.brackt.providers.ibmq.api.models.NetworkProject +import net.javaman.brackt.providers.ibmq.api.models.NetworksResponse +import net.javaman.brackt.providers.ibmq.api.models.NewRequest +import net.javaman.brackt.providers.ibmq.api.models.NewResponse +import net.javaman.brackt.providers.ibmq.api.models.ResultResponse +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentBackend +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentQasm +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentResponse +import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest +import net.javaman.brackt.providers.ibmq.api.models.VersionsResponse +import net.javaman.brackt.providers.ibmq.transpiler.toQasm +import kotlin.js.ExperimentalJsExport +import kotlin.js.JsExport +import kotlin.js.JsName +import kotlin.jvm.JvmStatic +import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds +import kotlin.time.ExperimentalTime + +/** + * An interface for [IbmqApi] that saves session data + */ +@Suppress("TooManyFunctions") // All are needed +open class IbmqProviderImpl { + private val ibmqApi: IbmqApiImpl by injection() + private val logger: Logger by injection() + + lateinit var accessToken: String + lateinit var userId: String + lateinit var network: NetworkHub + lateinit var group: NetworkGroup + lateinit var project: NetworkProject + lateinit var device: BackendResponse + lateinit var code: Code + lateinit var jobId: String + + companion object { + private const val DEVICE_STATUS_ACTIVE = "active" + private const val JOB_STATUS_COMPLETE = "COMPLETED" + private const val JOB_STATUS_CANCELLED = "CANCELLED" + + @OptIn(ExperimentalTime::class) + @JvmStatic + val JOB_TIMEOUT_DURATION = 5.minutes + + @OptIn(ExperimentalTime::class) + @JvmStatic + val JOB_POLL_INTERVAL = 2.seconds + + @OptIn(ExperimentalTime::class) + @JvmStatic + private val JOB_LOG_INTERVAL = 10.seconds + + /** + * Add injections for this module + */ + @JvmStatic + fun addInjections() = InjectionManager.add { + one { IbmqApiImpl() } + one { IbmqApi() } + one { IbmqProviderImpl() } + one { IbmqProvider() } + } + } + + /** + * Log in using an API token; for JVM, pass an environment variable from [PropertyManager] + */ + suspend fun logIn(apiToken: String): LogInWithTokenResponse { + val request = LogInWithTokenRequest(apiToken) + val response = ibmqApi.logInWithToken(request) + accessToken = response.id + userId = response.userId + return response + } + + /** + * Get the API token associated with this session + */ + suspend fun getApiToken() = ibmqApi.apiToken(accessToken, userId) + + /** + * Select the default network, including the default group and project + */ + suspend fun selectNetwork() = selectNetwork { networks -> + network = networks.first { it.isDefault } + val groupEntry = network.groups.entries.first { it.value.isDefault } + group = groupEntry.value + val projectEntry = groupEntry.value.projects.entries.first { it.value.isDefault } + project = projectEntry.value + } + + /** + * Select a network manually using a lambda + */ + @JsName("selectNetworkWithPicker") + suspend fun selectNetwork(picker: (NetworksResponse) -> Unit) { + val response = ibmqApi.networks(accessToken) + picker(response) + logger.info { "Selected network with name (${network.name})" } + logger.info { "Selected group with name (${group.name})" } + logger.info { "Selected project with name (${project.name})" } + } + + /** + * Select a device; default choice predicates: + * - Simulator + * - 5 or more qubits + * - Smallest queue + */ + suspend fun selectDevice(simulator: Boolean = true, minQubits: Int = 5) = selectDevice { devices -> + val projectDeviceNames = project.devices.values.map { it.name } + device = devices.filter { + projectDeviceNames.contains(it.name) && + it.isSimulator == simulator && + it.qubits >= minQubits && + it.deviceStatus.state && + it.deviceStatus.status == DEVICE_STATUS_ACTIVE + }.minByOrNull { + it.queueLength + } ?: throw DeviceNotAvailableException( + "Could not select a device because none matched the default predicates" + ) + } + + /** + * Select a device using a custom lambda + */ + @JsName("selectDeviceWithPicker") + suspend fun selectDevice(picker: (BackendsResponse) -> Unit) { + val response = ibmqApi.backends(accessToken) + picker(response) + logger.info { "Selected device with name (${device.name})" } + } + + /** + * Get the currently running jobs + */ + suspend fun getJobs() = ibmqApi.jobs(accessToken) + + /** + * Get the number of currently running and maximum number of jobs + */ + suspend fun getJobsLimit() = + ibmqApi.jobsLimit(accessToken, network.name, group.name, project.name, device.name) + + /** + * Run an experiment on a device + */ + @JsName("runExperimentWithRequest") + suspend fun runExperiment(request: RunExperimentRequest): RunExperimentResponse { + val response = ibmqApi.runExperiment( + accessToken, + request, + network.name, + group.name, + project.name + ) + jobId = response.id + return response + } + + /** + * Run a [QuantumCircuit] on a device + */ + suspend fun runExperiment(qc: QuantumCircuit, shots: Int = 1024, name: String = ""): RunExperimentResponse { + return runExperiment( + RunExperimentRequest( + qasms = listOf( + RunExperimentQasm( + qasm = qc.toQasm() + ) + ), + backend = RunExperimentBackend( + name = device.name + ), + codeId = "", + shots = shots, + name = name + ) + ) + } + + /** + * Upload a new version to a saved circuit + */ + suspend fun updateExperiment(codeId: String, request: VersionsRequest): VersionsResponse { + val response = ibmqApi.versions(accessToken, request, codeId) + code = response + return response + } + + /** + * Get the latest version of a circuit + */ + suspend fun getExperiment(codeId: String): LatestResponse { + val response = ibmqApi.latest(accessToken, codeId) + code = response + return response + } + + /** + * Get all saved circuits + */ + suspend fun getExperiments() = ibmqApi.lastest(accessToken, userId) + + /** + * Save a new circuit + */ + suspend fun newExperiment(request: NewRequest): NewResponse { + val response = ibmqApi.newCode(accessToken, request) + code = response + return response + } + + /** + * Get the status of a run + */ + suspend fun getRun(jobId: String) = ibmqApi.job(accessToken, jobId) + + /** + * Get a URL for the results of a run + */ + suspend fun getResultDownloadUrl(jobId: String) = ibmqApi.resultDownloadUrl(accessToken, jobId) + + /** + * Get the results of a run from a URL + */ + suspend fun getResult(url: String): ResultResponse { + logger.info { "Getting results from a URL" } + val response = client.get(url) + logger.info { "Received results download with size (${response.results.size})" } + return response + } + + @OptIn(ExperimentalTime::class) + suspend fun andWait(timeoutDuration: Duration = JOB_TIMEOUT_DURATION): ResultResponse { + val tStart = Clock.System.now() + var runResponse = getRun(jobId) + var tEnd = Clock.System.now() + var tLastLog = Clock.System.now() + while (runResponse.status != JOB_STATUS_COMPLETE && runResponse.status != JOB_STATUS_CANCELLED) { + delay(JOB_POLL_INTERVAL) + runResponse = getRun(jobId) + tEnd = Clock.System.now() + if (tEnd.minus(tLastLog) > JOB_LOG_INTERVAL) { + logger.info { "Time elapsed since job was requested: (${tEnd.minus(tStart).inWholeSeconds}) seconds" } + tLastLog = tEnd + } + if (tEnd.minus(tStart) > timeoutDuration) { + throw JobTimeoutException( + "Job timed out after duration (${tEnd.minus(tStart).inWholeSeconds}) seconds; " + + "the job is probably still active, but we're impatient!" + ) + } + } + if (runResponse.status == JOB_STATUS_CANCELLED) throw JobCancelledException("Job was cancelled") + logger.info { + "Total time elapsed from job request to response: (${tEnd.minus(tStart).inWholeSeconds}) seconds" + } + val resultDownloadUrl = getResultDownloadUrl(jobId).url.decodedUrl() + val result = getResult(resultDownloadUrl) + logger.info { result.results[0].data.pretty() } + return result + } + + /** + * Run an experiment and wait for the job to complete + */ + @OptIn(ExperimentalTime::class) + @JsName("runExperimentAndWaitWithRequest") + suspend fun runExperimentAndWait( + request: RunExperimentRequest, + timeoutDuration: Duration = JOB_TIMEOUT_DURATION + ): ResultResponse { + runExperiment(request) + return andWait(timeoutDuration) + } + + /** + * Run an experiment and wait for the job to complete + */ + @OptIn(ExperimentalTime::class) + suspend fun runExperimentAndWait( + qc: QuantumCircuit, + timeoutDuration: Duration = JOB_TIMEOUT_DURATION + ): ResultResponse { + runExperiment(qc) + return andWait(timeoutDuration) + } +} + +@Suppress("UtilityClassWithPublicConstructor") // It's used in the `actual` implementations +expect class IbmqProvider() { + companion object : InjectionAdder +} + +@JsExport +class DeviceNotAvailableException(message: String) : Exception(message) + +@JsExport +class JobTimeoutException(message: String) : Exception(message) + +@JsExport +class JobCancelledException(message: String) : Exception(message) diff --git a/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt b/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt deleted file mode 100644 index 7df3f08..0000000 --- a/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt +++ /dev/null @@ -1,254 +0,0 @@ -@file:OptIn(ExperimentalJsExport::class) - -package net.javaman.brackt.providers.ibmq.api - -import io.ktor.client.request.get -import io.ktor.client.request.headers -import io.ktor.client.request.post -import io.ktor.http.ContentType -import io.ktor.http.contentType -import net.javaman.brackt.api.util.formatters.censor -import net.javaman.brackt.api.util.injections.injection -import net.javaman.brackt.api.util.logging.Logger -import net.javaman.brackt.providers.ibmq.api.models.ApiTokenResponse -import net.javaman.brackt.providers.ibmq.api.models.BackendsResponse -import net.javaman.brackt.providers.ibmq.api.models.JobResponse -import net.javaman.brackt.providers.ibmq.api.models.JobsLimitResponse -import net.javaman.brackt.providers.ibmq.api.models.JobsResponse -import net.javaman.brackt.providers.ibmq.api.models.LastestResponse -import net.javaman.brackt.providers.ibmq.api.models.LatestResponse -import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenRequest -import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenResponse -import net.javaman.brackt.providers.ibmq.api.models.NetworksResponse -import net.javaman.brackt.providers.ibmq.api.models.NewRequest -import net.javaman.brackt.providers.ibmq.api.models.NewResponse -import net.javaman.brackt.providers.ibmq.api.models.ResultDownloadUrlResponse -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentResponse -import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest -import net.javaman.brackt.providers.ibmq.api.models.VersionsResponse -import kotlin.js.ExperimentalJsExport -import kotlin.js.JsExport - -/** - * An HTTP client for [IBM Quantum](https://quantum-computing.ibm.com/) - */ -@Suppress("TooManyFunctions") // Necessary functions -@JsExport -class IbmqApi { - val logger: Logger by injection() - - companion object { - const val BASE_AUTH_URL = "https://auth.quantum-computing.ibm.com/api" - const val BASE_API_URL = "https://api.quantum-computing.ibm.com/api" - const val ACCESS_TOKEN_HEADER = "x-access-token" - } -} - -/** - * Request an access (session) token using an API token - */ -suspend fun IbmqApi.logInWithToken(request: LogInWithTokenRequest): LogInWithTokenResponse { - logger.info { "Requesting access token with apiToken (${request.apiToken.censor()})" } - val response = client.post("${IbmqApi.BASE_AUTH_URL}/users/loginWithToken") { - contentType(ContentType.Application.Json) - body = request - } - logger.info { "Received accessToken (${response.id.censor()})" } - return response -} - -/** - * Request the original API token that created this access token - */ -suspend fun IbmqApi.apiToken(accessToken: String, userId: String): ApiTokenResponse { - logger.info { "Requesting API token with userId (${userId.censor()})" } - val response = client.get("${IbmqApi.BASE_AUTH_URL}/users/$userId/apiToken") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - } - logger.info { "Received apiToken (${response.apiToken.censor()})" } - return response -} - -/** - * Get all available networks, including groups and projects within the network - */ -suspend fun IbmqApi.networks(accessToken: String): NetworksResponse { - logger.info { "Requesting networks" } - val response = client.get("${IbmqApi.BASE_API_URL}/network") { - headers.append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - logger.info { "Received a list networks with size (${response.size})" } - return response -} - -/** - * Get available devices for the selected network/group/project - */ -suspend fun IbmqApi.backends(accessToken: String): BackendsResponse { - logger.info { "Requesting backends" } - val response = client.get("${IbmqApi.BASE_API_URL}/users/backends") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - } - logger.info { "Received a list of backends with size (${response.size})" } - return response -} - -/** - * Get queued or running jobs - */ -suspend fun IbmqApi.jobs(accessToken: String): JobsResponse { - logger.info { "Requesting jobs" } - val response = client.get("${IbmqApi.BASE_API_URL}/jobs/v2") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - } - logger.info { "Received a list of jobs with size (${response.items.size})" } - return response -} - -/** - * Get the limit of jobs in the queue - */ -suspend fun IbmqApi.jobsLimit( - accessToken: String, - network: String, - group: String, - project: String, - device: String -): JobsLimitResponse { - logger.info { "Requesting jobs limit" } - val response = client.get( - "${IbmqApi.BASE_API_URL}/network/$network/groups/$group" + - "/projects/$project/devices/$device/jobsLimit" - ) { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - } - logger.info { - "Received a jobs limit of runningJobs (${response.runningJobs}) / maximumJobs (${response.maximumJobs})" - } - return response -} - -/** - * Run QASM code on a device - */ -suspend fun IbmqApi.runExperiment( - accessToken: String, - request: RunExperimentRequest, - network: String, - group: String, - project: String -): RunExperimentResponse { - logger.info { - "Attempting to run an experiment with idCode (${request.codeId}) " + - "on backend with name (${request.backend.name})" - } - val response = client.post( - "${IbmqApi.BASE_API_URL}/network/$network/groups/$group/" + - "projects/$project/runExperiment" - ) { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - contentType(ContentType.Application.Json) - body = request - } - logger.info { "Received a job with id (${response.id})" } - return response -} - -/** - * Upload the latest version of a saved code (circuit) - */ -suspend fun IbmqApi.versions(accessToken: String, request: VersionsRequest, code: String): VersionsResponse { - logger.info { "Attempting to update a version with codeId (${request.idCode})" } - val response = client.post("${IbmqApi.BASE_API_URL}/codes/$code/versions") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - contentType(ContentType.Application.Json) - body = request - } - logger.info { "Received updated version with codeId (${request.idCode})" } - return response -} - -/** - * Get the latest version of a saved code - */ -suspend fun IbmqApi.latest(accessToken: String, codeId: String): LatestResponse { - logger.info { "Requesting latest version of code ($codeId)" } - val response = client.get("${IbmqApi.BASE_API_URL}/codes/$codeId/versions/latest") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - } - logger.info { "Received version (${response.versionId}) of code ($codeId)" } - return response -} - -/** - * Get all saved codes - */ -suspend fun IbmqApi.lastest(accessToken: String, userId: String): LastestResponse { - logger.info { "Request lastest codes for userId (${userId.censor()})" } - val response = client.get("${IbmqApi.BASE_API_URL}/users/$userId/codes/lastest") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - } - logger.info { "Received codes with count (${response.count})" } - return response -} - -/** - * Upload a new code - */ -suspend fun IbmqApi.newCode(accessToken: String, request: NewRequest): NewResponse { - logger.info { "Attempting to create new code" } - val response = client.post("${IbmqApi.BASE_API_URL}/codes") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - contentType(ContentType.Application.Json) - body = request - } - logger.info { "Received code with codeId (${response.idCode})" } - return response -} - -/** - * Get the status of a specific job - */ -suspend fun IbmqApi.job(accessToken: String, jobId: String): JobResponse { - logger.info { "Requesting the status of job ($jobId)" } - val response = client.get("${IbmqApi.BASE_API_URL}/jobs/$jobId/v/1") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - } - logger.info { "Received job with status (${response.status})" } - return response -} - -/** - * Get a URL to download the results of a job - */ -suspend fun IbmqApi.resultDownloadUrl(accessToken: String, jobId: String): ResultDownloadUrlResponse { - logger.info { "Requesting result download URL for job ($jobId)" } - val response = client.get("${IbmqApi.BASE_API_URL}/jobs/$jobId/resultDownloadUrl") { - headers { - append(IbmqApi.ACCESS_TOKEN_HEADER, accessToken) - } - } - logger.info { "Received result download URL" } - return response -} diff --git a/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiImpl.kt b/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiImpl.kt new file mode 100644 index 0000000..2d8490b --- /dev/null +++ b/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiImpl.kt @@ -0,0 +1,251 @@ +package net.javaman.brackt.providers.ibmq.api + +import io.ktor.client.request.get +import io.ktor.client.request.headers +import io.ktor.client.request.post +import io.ktor.http.ContentType +import io.ktor.http.contentType +import net.javaman.brackt.api.util.formatters.censor +import net.javaman.brackt.api.util.injections.injection +import net.javaman.brackt.api.util.logging.Logger +import net.javaman.brackt.providers.ibmq.api.models.ApiTokenResponse +import net.javaman.brackt.providers.ibmq.api.models.BackendsResponse +import net.javaman.brackt.providers.ibmq.api.models.JobResponse +import net.javaman.brackt.providers.ibmq.api.models.JobsLimitResponse +import net.javaman.brackt.providers.ibmq.api.models.JobsResponse +import net.javaman.brackt.providers.ibmq.api.models.LastestResponse +import net.javaman.brackt.providers.ibmq.api.models.LatestResponse +import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenRequest +import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenResponse +import net.javaman.brackt.providers.ibmq.api.models.NetworksResponse +import net.javaman.brackt.providers.ibmq.api.models.NewRequest +import net.javaman.brackt.providers.ibmq.api.models.NewResponse +import net.javaman.brackt.providers.ibmq.api.models.ResultDownloadUrlResponse +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentResponse +import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest +import net.javaman.brackt.providers.ibmq.api.models.VersionsResponse + +/** + * An HTTP client for [IBM Quantum](https://quantum-computing.ibm.com/) + */ +@Suppress("TooManyFunctions") +open class IbmqApiImpl { + private val logger: Logger by injection() + + companion object { + private const val BASE_AUTH_URL = "https://auth.quantum-computing.ibm.com/api" + private const val BASE_API_URL = "https://api.quantum-computing.ibm.com/api" + private const val ACCESS_TOKEN_HEADER = "x-access-token" + } + + /** + * Request an access (session) token using an API token + */ + suspend fun logInWithToken(request: LogInWithTokenRequest): LogInWithTokenResponse { + logger.info { "Requesting access token with apiToken (${request.apiToken.censor()})" } + val response = client.post("${BASE_AUTH_URL}/users/loginWithToken") { + contentType(ContentType.Application.Json) + body = request + } + logger.info { "Received accessToken (${response.id.censor()})" } + return response + } + + /** + * Request the original API token that created this access token + */ + suspend fun apiToken(accessToken: String, userId: String): ApiTokenResponse { + logger.info { "Requesting API token with userId (${userId.censor()})" } + val response = client.get("${BASE_AUTH_URL}/users/$userId/apiToken") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + } + logger.info { "Received apiToken (${response.apiToken.censor()})" } + return response + } + + /** + * Get all available networks, including groups and projects within the network + */ + suspend fun networks(accessToken: String): NetworksResponse { + logger.info { "Requesting networks" } + val response = client.get("${BASE_API_URL}/network") { + headers.append(ACCESS_TOKEN_HEADER, accessToken) + } + logger.info { "Received a list networks with size (${response.size})" } + return response + } + + /** + * Get available devices for the selected network/group/project + */ + suspend fun backends(accessToken: String): BackendsResponse { + logger.info { "Requesting backends" } + val response = client.get("${BASE_API_URL}/users/backends") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + } + logger.info { "Received a list of backends with size (${response.size})" } + return response + } + + /** + * Get queued or running jobs + */ + suspend fun jobs(accessToken: String): JobsResponse { + logger.info { "Requesting jobs" } + val response = client.get("${BASE_API_URL}/jobs/v2") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + } + logger.info { "Received a list of jobs with size (${response.items.size})" } + return response + } + + /** + * Get the limit of jobs in the queue + */ + suspend fun jobsLimit( + accessToken: String, + network: String, + group: String, + project: String, + device: String + ): JobsLimitResponse { + logger.info { "Requesting jobs limit" } + val response = client.get( + "${BASE_API_URL}/network/$network/groups/$group" + + "/projects/$project/devices/$device/jobsLimit" + ) { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + } + logger.info { + "Received a jobs limit of runningJobs (${response.runningJobs}) / maximumJobs (${response.maximumJobs})" + } + return response + } + + /** + * Run QASM code on a device + */ + suspend fun runExperiment( + accessToken: String, + request: RunExperimentRequest, + network: String, + group: String, + project: String + ): RunExperimentResponse { + logger.info { + "Attempting to run an experiment with idCode (${request.codeId}) " + + "on backend with name (${request.backend.name})" + } + val response = client.post( + "${BASE_API_URL}/network/$network/groups/$group/" + + "projects/$project/runExperiment" + ) { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + contentType(ContentType.Application.Json) + body = request + } + logger.info { "Received a job with id (${response.id})" } + return response + } + + /** + * Upload the latest version of a saved code (circuit) + */ + suspend fun versions(accessToken: String, request: VersionsRequest, code: String): VersionsResponse { + logger.info { "Attempting to update a version with codeId (${request.idCode})" } + val response = client.post("${BASE_API_URL}/codes/$code/versions") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + contentType(ContentType.Application.Json) + body = request + } + logger.info { "Received updated version with codeId (${request.idCode})" } + return response + } + + /** + * Get the latest version of a saved code + */ + suspend fun latest(accessToken: String, codeId: String): LatestResponse { + logger.info { "Requesting latest version of code ($codeId)" } + val response = client.get("${BASE_API_URL}/codes/$codeId/versions/latest") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + } + logger.info { "Received version (${response.versionId}) of code ($codeId)" } + return response + } + + /** + * Get all saved codes + */ + suspend fun lastest(accessToken: String, userId: String): LastestResponse { + logger.info { "Request lastest codes for userId (${userId.censor()})" } + val response = client.get("${BASE_API_URL}/users/$userId/codes/lastest") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + } + logger.info { "Received codes with count (${response.count})" } + return response + } + + /** + * Upload a new code + */ + suspend fun newCode(accessToken: String, request: NewRequest): NewResponse { + logger.info { "Attempting to create new code" } + val response = client.post("${BASE_API_URL}/codes") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + contentType(ContentType.Application.Json) + body = request + } + logger.info { "Received code with codeId (${response.idCode})" } + return response + } + + /** + * Get the status of a specific job + */ + suspend fun job(accessToken: String, jobId: String): JobResponse { + logger.info { "Requesting the status of job ($jobId)" } + val response = client.get("${BASE_API_URL}/jobs/$jobId/v/1") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + } + logger.info { "Received job with status (${response.status})" } + return response + } + + /** + * Get a URL to download the results of a job + */ + suspend fun resultDownloadUrl(accessToken: String, jobId: String): ResultDownloadUrlResponse { + logger.info { "Requesting result download URL for job ($jobId)" } + val response = client.get("${BASE_API_URL}/jobs/$jobId/resultDownloadUrl") { + headers { + append(ACCESS_TOKEN_HEADER, accessToken) + } + } + logger.info { "Received result download URL" } + return response + } +} + +expect class IbmqApi() diff --git a/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/transpiler/QasmTranspiler.kt b/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/transpiler/QasmTranspiler.kt index 74da5d2..4b8cf06 100644 --- a/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/transpiler/QasmTranspiler.kt +++ b/brac-kt-ibmq-provider/src/commonMain/kotlin/net/javaman/brackt/providers/ibmq/transpiler/QasmTranspiler.kt @@ -5,7 +5,7 @@ package net.javaman.brackt.providers.ibmq.transpiler import net.javaman.brackt.api.quantum.QuantumCircuit import net.javaman.brackt.api.quantum.QuantumGate import net.javaman.brackt.api.quantum.QuantumMacro -import net.javaman.brackt.api.quantum.QubitMap +import net.javaman.brackt.api.util.reflection.getPlatformName import kotlin.js.ExperimentalJsExport import kotlin.js.JsExport import kotlin.math.max @@ -35,6 +35,6 @@ fun QuantumCircuit.toQasm(): String = (listOf( runMacro { QuantumMacro.cz onQubits listOf(it.qubit1, it.qubit2) } }.toQasm() is QuantumGate.Measure -> "measure q[${it.qubit}] -> c[${it.bit}]" - else -> throw UnsupportedOperationException("Unsupported gate (${it::class.simpleName})") + else -> throw UnsupportedOperationException("Unsupported gate (${it::class.getPlatformName()})") } }).joinToString(";") + ";" diff --git a/brac-kt-ibmq-provider/src/commonTest/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderTest.kt b/brac-kt-ibmq-provider/src/commonTest/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderTest.kt index 7dc92d4..1d5b019 100644 --- a/brac-kt-ibmq-provider/src/commonTest/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderTest.kt +++ b/brac-kt-ibmq-provider/src/commonTest/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderTest.kt @@ -12,13 +12,13 @@ import net.javaman.brackt.providers.ibmq.TestData.RUN_EXPERIMENT_REQUEST import kotlin.test.Test class IbmqProviderTest { - private val ibmqProvider: IbmqProvider by injection() + private val ibmqProvider: IbmqProviderImpl by injection() private val propertyManager: PropertyManager by injection() private val logger: Logger by injection() init { BracKtApi.addInjections() - IbmqProvider.addInjections() + IbmqProviderImpl.addInjections() } // kotlin-test-common does not support test ordering, so we must run all the tests in one function diff --git a/brac-kt-ibmq-provider/src/commonTest/kotlin/net/javaman/brackt/providers/ibmq/TestData.kt b/brac-kt-ibmq-provider/src/commonTest/kotlin/net/javaman/brackt/providers/ibmq/TestData.kt index 708cc5a..6b05934 100644 --- a/brac-kt-ibmq-provider/src/commonTest/kotlin/net/javaman/brackt/providers/ibmq/TestData.kt +++ b/brac-kt-ibmq-provider/src/commonTest/kotlin/net/javaman/brackt/providers/ibmq/TestData.kt @@ -9,7 +9,7 @@ import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest import kotlin.jvm.JvmStatic object TestData { - private val ibmqProvider: IbmqProvider by injection() + private val ibmqProvider: IbmqProviderImpl by injection() const val NETWORK = "ibm-q" const val GROUP = "open" diff --git a/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt b/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt new file mode 100644 index 0000000..44278c1 --- /dev/null +++ b/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt @@ -0,0 +1,121 @@ +@file:OptIn(DelicateCoroutinesApi::class, ExperimentalJsExport::class) + +package net.javaman.brackt.providers.ibmq + +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise +import net.javaman.brackt.api.quantum.QuantumCircuit +import net.javaman.brackt.api.util.injections.InjectionAdder +import net.javaman.brackt.api.util.injections.injection +import net.javaman.brackt.providers.ibmq.IbmqProviderImpl.Companion.JOB_TIMEOUT_DURATION +import net.javaman.brackt.providers.ibmq.api.models.BackendsResponse +import net.javaman.brackt.providers.ibmq.api.models.NetworksResponse +import net.javaman.brackt.providers.ibmq.api.models.NewRequest +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest +import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest +import kotlin.time.Duration + +// Due to a Kotlin/JS bug when compiling companion objects, we must redeclare this static method +@JsExport +fun addInjections() = IbmqProvider.addInjections() + +@Suppress("TooManyFunctions") +@JsExport +actual class IbmqProvider { + private val ibmqProviderImpl: IbmqProviderImpl by injection() + + actual companion object : InjectionAdder { + override fun addInjections() = IbmqProviderImpl.addInjections() + } + + fun logInAsync(apiToken: String) = GlobalScope.promise { ibmqProviderImpl.logIn(apiToken) } + + fun getApiTokenAsync() = GlobalScope.promise { ibmqProviderImpl.getApiToken() } + + fun selectNetworkAsync() = GlobalScope.promise { ibmqProviderImpl.selectNetwork() } + + @JsName("selectNetworkWithPickerAsync") + fun selectNetworkAsync( + picker: (NetworksResponse) -> Unit + ) = GlobalScope.promise { ibmqProviderImpl.selectNetwork(picker) } + + fun selectDeviceAsync( + simulator: Boolean = true, + minQubits: Int = 5 + ) = GlobalScope.promise { ibmqProviderImpl.selectDevice(simulator, minQubits) } + + @JsName("selectDeviceWithPickerAsync") + fun selectDeviceAsync( + picker: (BackendsResponse) -> Unit + ) = GlobalScope.promise { ibmqProviderImpl.selectDevice(picker) } + + fun getJobsAsync() = GlobalScope.promise { ibmqProviderImpl.getJobs() } + + fun getJobsLimitAsync() = GlobalScope.promise { ibmqProviderImpl.getJobsLimit() } + + @JsName("runExperimentWithRequestAsync") + fun runExperimentAsync( + request: RunExperimentRequest + ) = GlobalScope.promise { ibmqProviderImpl.runExperiment(request) } + + fun runExperimentAsync( + qc: QuantumCircuit + ) = GlobalScope.promise { ibmqProviderImpl.runExperiment(qc) } + + @JsName("runExperimentAsyncWithShots") + fun runExperimentAsync( + qc: QuantumCircuit, + shots: Int = 1024 + ) = GlobalScope.promise { ibmqProviderImpl.runExperiment(qc, shots) } + + @JsName("runExperimentAsyncWithShotsAndName") + fun runExperimentAsync( + qc: QuantumCircuit, + shots: Int = 1024, + name: String = "" + ) = GlobalScope.promise { ibmqProviderImpl.runExperiment(qc, shots, name) } + + fun updateExperimentAsync( + codeId: String, + request: VersionsRequest + ) = GlobalScope.promise { ibmqProviderImpl.updateExperiment(codeId, request) } + + fun getExperimentAsync(codeId: String) = GlobalScope.promise { ibmqProviderImpl.getExperiment(codeId) } + + fun getExperimentsAsync() = GlobalScope.promise { ibmqProviderImpl.getExperiments() } + + fun newExperimentAsync(request: NewRequest) = GlobalScope.promise { ibmqProviderImpl.newExperiment(request) } + + fun getRunAsync(jobId: String) = GlobalScope.promise { ibmqProviderImpl.getRun(jobId) } + + fun getResultDownloadUrlAsync(jobId: String) = GlobalScope.promise { ibmqProviderImpl.getResultDownloadUrl(jobId) } + + fun getResultAsync(url: String) = GlobalScope.promise { ibmqProviderImpl.getResult(url) } + + fun andWaitAsync() = GlobalScope.promise { ibmqProviderImpl.andWait() } + + @JsName("andWaitWithTimeoutAsync") + fun andWaitAsync(timeoutDuration: Duration) = GlobalScope.promise { ibmqProviderImpl.andWait(timeoutDuration) } + + fun runExperimentAndWaitAsync( + qc: QuantumCircuit + ) = GlobalScope.promise { ibmqProviderImpl.runExperimentAndWait(qc) } + + @JsName("runExperimentAndWaitWithTimeoutAsync") + fun runExperimentAndWaitAsync( + qc: QuantumCircuit, + timeoutDuration: Duration + ) = GlobalScope.promise { ibmqProviderImpl.runExperimentAndWait(qc, timeoutDuration) } + + @JsName("runExperimentAndWaitWithRequestAsync") + fun runExperimentAndWaitAsync( + request: RunExperimentRequest + ) = GlobalScope.promise { ibmqProviderImpl.runExperimentAndWait(request) } + + @JsName("runExperimentAndWaitWithRequestAndTimeoutAsync") + fun runExperimentAndWaitAsync( + request: RunExperimentRequest, + timeoutDuration: Duration + ) = GlobalScope.promise { ibmqProviderImpl.runExperimentAndWait(request, timeoutDuration) } +} diff --git a/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderPlatform.kt b/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderPlatform.kt deleted file mode 100644 index 654f28c..0000000 --- a/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderPlatform.kt +++ /dev/null @@ -1,81 +0,0 @@ -@file:OptIn(DelicateCoroutinesApi::class, ExperimentalJsExport::class) -@file:JsExport - -package net.javaman.brackt.providers.ibmq - -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.promise -import net.javaman.brackt.api.quantum.QuantumCircuit -import net.javaman.brackt.providers.ibmq.api.models.BackendsResponse -import net.javaman.brackt.providers.ibmq.api.models.NetworksResponse -import net.javaman.brackt.providers.ibmq.api.models.NewRequest -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest -import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest -import kotlin.time.Duration - -fun IbmqProvider.logInAsync(apiToken: String) = GlobalScope.promise { logIn(apiToken) } - -fun IbmqProvider.getApiTokenAsync() = GlobalScope.promise { getApiToken() } - -fun IbmqProvider.selectNetworkAsync() = GlobalScope.promise { selectNetwork() } - -@JsName("selectNetworkWithPickerAsync") -fun IbmqProvider.selectNetworkAsync( - picker: IbmqProvider.(NetworksResponse) -> Unit -) = GlobalScope.promise { selectNetwork(picker) } - -fun IbmqProvider.selectDeviceAsync( - simulator: Boolean = true, - minQubits: Int = 5 -) = GlobalScope.promise { selectDevice(simulator, minQubits) } - -@JsName("selectDeviceWithPickerAsync") -fun IbmqProvider.selectDeviceAsync( - picker: IbmqProvider.(BackendsResponse) -> Unit -) = GlobalScope.promise { selectDevice(picker) } - -fun IbmqProvider.getJobsAsync() = GlobalScope.promise { getJobs() } - -fun IbmqProvider.getJobsLimitAsync() = GlobalScope.promise { getJobsLimit() } - -@JsName("runExperimentWithRequestAsync") -fun IbmqProvider.runExperimentAsync(request: RunExperimentRequest) = GlobalScope.promise { runExperiment(request) } - -fun IbmqProvider.runExperimentAsync( - qc: QuantumCircuit, - shots: Int = 1024, - name: String = "" -) = GlobalScope.promise { runExperiment(qc, shots, name) } - -fun IbmqProvider.updateExperimentAsync( - codeId: String, - request: VersionsRequest -) = GlobalScope.promise { updateExperiment(codeId, request) } - -fun IbmqProvider.getExperimentAsync(codeId: String) = GlobalScope.promise { getExperiment(codeId) } - -fun IbmqProvider.getExperimentsAsync() = GlobalScope.promise { getExperiments() } - -fun IbmqProvider.newExperimentAsync(request: NewRequest) = GlobalScope.promise { newExperiment(request) } - -fun IbmqProvider.getRunAsync(jobId: String) = GlobalScope.promise { getRun(jobId) } - -fun IbmqProvider.getResultDownloadUrlAsync(jobId: String) = GlobalScope.promise { getResultDownloadUrl(jobId) } - -fun IbmqProvider.getResultAsync(url: String) = GlobalScope.promise { getResult(url) } - -fun IbmqProvider.andWaitAsync( - timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION -) = GlobalScope.promise { andWait(timeoutDuration) } - -@JsName("runExperimentAndWaitWithRequestAsync") -fun IbmqProvider.runExperimentAndWaitAsync( - request: RunExperimentRequest, - timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION -) = GlobalScope.promise { runExperimentAndWait(request, timeoutDuration) } - -fun IbmqProvider.runExperimentAndWaitAsync( - qc: QuantumCircuit, - timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION -) = GlobalScope.promise { runExperimentAndWait(qc, timeoutDuration) } diff --git a/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt b/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt new file mode 100644 index 0000000..ab39b90 --- /dev/null +++ b/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt @@ -0,0 +1,70 @@ +@file:OptIn(DelicateCoroutinesApi::class, ExperimentalJsExport::class) + +package net.javaman.brackt.providers.ibmq.api + +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise +import net.javaman.brackt.api.util.injections.injection +import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenRequest +import net.javaman.brackt.providers.ibmq.api.models.NewRequest +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest +import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest + +@Suppress("TooManyFunctions") +@JsExport +actual class IbmqApi { + private val ibmqApiImpl: IbmqApiImpl by injection() + + fun logInWithTokenAsync(request: LogInWithTokenRequest) = + GlobalScope.promise { ibmqApiImpl.logInWithToken(request) } + + fun apiTokenAsync( + accessToken: String, + userId: String + ) = GlobalScope.promise { ibmqApiImpl.apiToken(accessToken, userId) } + + fun networksAsync(accessToken: String) = GlobalScope.promise { ibmqApiImpl.networks(accessToken) } + + fun backendsAsync(accessToken: String) = GlobalScope.promise { ibmqApiImpl.backends(accessToken) } + + fun jobsAsync(accessToken: String) = GlobalScope.promise { ibmqApiImpl.jobs(accessToken) } + + fun jobsLimitAsync( + accessToken: String, + network: String, + group: String, + project: String, + device: String + ) = GlobalScope.promise { ibmqApiImpl.jobsLimit(accessToken, network, group, project, device) } + + fun runExperimentAsync( + accessToken: String, + request: RunExperimentRequest, + network: String, + group: String, + project: String + ) = GlobalScope.promise { ibmqApiImpl.runExperiment(accessToken, request, network, group, project) } + + fun versionsAsync( + accessToken: String, + request: VersionsRequest, + code: String + ) = GlobalScope.promise { ibmqApiImpl.versions(accessToken, request, code) } + + fun latestAsync(accessToken: String, codeId: String) = + GlobalScope.promise { ibmqApiImpl.latest(accessToken, codeId) } + + fun lastestAsync(accessToken: String, userId: String) = + GlobalScope.promise { ibmqApiImpl.lastest(accessToken, userId) } + + fun newCodeAsync(accessToken: String, request: NewRequest) = + GlobalScope.promise { ibmqApiImpl.newCode(accessToken, request) } + + fun jobAsync(accessToken: String, jobId: String) = GlobalScope.promise { ibmqApiImpl.job(accessToken, jobId) } + + fun resultDownloadUrlAsync( + accessToken: String, + jobId: String + ) = GlobalScope.promise { ibmqApiImpl.resultDownloadUrl(accessToken, jobId) } +} diff --git a/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiPlatform.kt b/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiPlatform.kt deleted file mode 100644 index 977ea7d..0000000 --- a/brac-kt-ibmq-provider/src/jsMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiPlatform.kt +++ /dev/null @@ -1,58 +0,0 @@ -@file:OptIn(DelicateCoroutinesApi::class, ExperimentalJsExport::class) -@file:JsExport - -package net.javaman.brackt.providers.ibmq.api - -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.promise -import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenRequest -import net.javaman.brackt.providers.ibmq.api.models.NewRequest -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest -import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest - -fun IbmqApi.logInWithTokenAsync(request: LogInWithTokenRequest) = GlobalScope.promise { logInWithToken(request) } - -fun IbmqApi.apiTokenAsync(accessToken: String, userId: String) = GlobalScope.promise { apiToken(accessToken, userId) } - -fun IbmqApi.networksAsync(accessToken: String) = GlobalScope.promise { networks(accessToken) } - -fun IbmqApi.backendsAsync(accessToken: String) = GlobalScope.promise { backends(accessToken) } - -fun IbmqApi.jobsAsync(accessToken: String) = GlobalScope.promise { jobs(accessToken) } - -fun IbmqApi.jobsLimitAsync( - accessToken: String, - network: String, - group: String, - project: String, - device: String -) = GlobalScope.promise { jobsLimit(accessToken, network, group, project, device) } - -fun IbmqApi.runExperimentAsync( - accessToken: String, - request: RunExperimentRequest, - network: String, - group: String, - project: String -) = GlobalScope.promise { runExperiment(accessToken, request, network, group, project) } - -fun IbmqApi.versionsAsync( - accessToken: String, - request: VersionsRequest, - code: String -) = GlobalScope.promise { versions(accessToken, request, code) } - -fun IbmqApi.latestAsync(accessToken: String, codeId: String) = GlobalScope.promise { latest(accessToken, codeId) } - -fun IbmqApi.lastestAsync(accessToken: String, userId: String) = GlobalScope.promise { lastest(accessToken, userId) } - -fun IbmqApi.newCodeAsync(accessToken: String, request: NewRequest) = - GlobalScope.promise { newCode(accessToken, request) } - -fun IbmqApi.jobAsync(accessToken: String, jobId: String) = GlobalScope.promise { job(accessToken, jobId) } - -fun IbmqApi.resultDownloadUrlAsync( - accessToken: String, - jobId: String -) = GlobalScope.promise { resultDownloadUrl(accessToken, jobId) } diff --git a/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt b/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt new file mode 100644 index 0000000..62dbd88 --- /dev/null +++ b/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProvider.kt @@ -0,0 +1,94 @@ +package net.javaman.brackt.providers.ibmq + +import kotlinx.coroutines.runBlocking +import net.javaman.brackt.api.quantum.QuantumCircuit +import net.javaman.brackt.api.util.injections.InjectionAdder +import net.javaman.brackt.api.util.injections.injection +import net.javaman.brackt.providers.ibmq.IbmqProviderImpl.Companion.JOB_TIMEOUT_DURATION +import net.javaman.brackt.providers.ibmq.api.models.BackendsResponse +import net.javaman.brackt.providers.ibmq.api.models.NetworksResponse +import net.javaman.brackt.providers.ibmq.api.models.NewRequest +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest +import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest +import kotlin.time.Duration + +@Suppress("TooManyFunctions") +actual class IbmqProvider { + private val ibmqProviderImpl: IbmqProviderImpl by injection() + + actual companion object : InjectionAdder { + override fun addInjections() = IbmqProviderImpl.addInjections() + } + + fun logInSync(apiToken: String) = runBlocking { ibmqProviderImpl.logIn(apiToken) } + + fun getApiTokenSync() = runBlocking { ibmqProviderImpl.getApiToken() } + + fun selectNetworkSync() = runBlocking { ibmqProviderImpl.selectNetwork() } + + fun selectNetworkSync( + picker: (NetworksResponse) -> Unit + ) = runBlocking { ibmqProviderImpl.selectNetwork(picker) } + + @JvmOverloads + fun selectDeviceSync( + simulator: Boolean = true, + minQubits: Int = 5 + ) = runBlocking { ibmqProviderImpl.selectDevice(simulator, minQubits) } + + fun selectDeviceSync( + picker: (BackendsResponse) -> Unit + ) = runBlocking { ibmqProviderImpl.selectDevice(picker) } + + fun getJobsSync() = runBlocking { ibmqProviderImpl.getJobs() } + + fun getJobsLimitSync() = runBlocking { ibmqProviderImpl.getJobsLimit() } + + fun runExperimentSync(request: RunExperimentRequest) = runBlocking { ibmqProviderImpl.runExperiment(request) } + + @JvmOverloads + fun runExperimentSync( + qc: QuantumCircuit, + shots: Int = 1024, + name: String = "" + ) = runBlocking { ibmqProviderImpl.runExperiment(qc, shots, name) } + + fun updateExperimentSync( + codeId: String, + request: VersionsRequest + ) = runBlocking { ibmqProviderImpl.updateExperiment(codeId, request) } + + fun getExperimentSync(codeId: String) = runBlocking { ibmqProviderImpl.getExperiment(codeId) } + + fun getExperimentsSync() = runBlocking { ibmqProviderImpl.getExperiments() } + + fun newExperimentSync(request: NewRequest) = runBlocking { ibmqProviderImpl.newExperiment(request) } + + fun getRunSync(jobId: String) = runBlocking { ibmqProviderImpl.getRun(jobId) } + + fun getResultDownloadUrlSync(jobId: String) = runBlocking { ibmqProviderImpl.getResultDownloadUrl(jobId) } + + fun getResultSync(url: String) = runBlocking { ibmqProviderImpl.getResult(url) } + + fun andWaitSync( + timeoutDuration: Duration = JOB_TIMEOUT_DURATION + ) = runBlocking { ibmqProviderImpl.andWait(timeoutDuration) } + + fun andWaitSync() = runBlocking { ibmqProviderImpl.andWait() } + + fun runExperimentAndWaitSync( + request: RunExperimentRequest, + timeoutDuration: Duration = JOB_TIMEOUT_DURATION + ) = runBlocking { ibmqProviderImpl.runExperimentAndWait(request, timeoutDuration) } + + fun runExperimentAndWaitSync( + request: RunExperimentRequest + ) = runBlocking { ibmqProviderImpl.runExperimentAndWait(request) } + + fun runExperimentAndWaitSync( + qc: QuantumCircuit, + timeoutDuration: Duration = JOB_TIMEOUT_DURATION + ) = runBlocking { ibmqProviderImpl.runExperimentAndWait(qc, timeoutDuration) } + + fun runExperimentAndWaitSync(qc: QuantumCircuit) = runBlocking { ibmqProviderImpl.runExperimentAndWait(qc) } +} diff --git a/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderPlatform.kt b/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderPlatform.kt deleted file mode 100644 index 9c0bc4b..0000000 --- a/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/IbmqProviderPlatform.kt +++ /dev/null @@ -1,80 +0,0 @@ -package net.javaman.brackt.providers.ibmq - -import kotlinx.coroutines.runBlocking -import net.javaman.brackt.api.quantum.QuantumCircuit -import net.javaman.brackt.providers.ibmq.api.models.BackendsResponse -import net.javaman.brackt.providers.ibmq.api.models.NetworksResponse -import net.javaman.brackt.providers.ibmq.api.models.NewRequest -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest -import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest -import kotlin.time.Duration - -fun IbmqProvider.logInSync(apiToken: String) = runBlocking { logIn(apiToken) } - -fun IbmqProvider.getApiTokenSync() = runBlocking { getApiToken() } - -fun IbmqProvider.selectNetworkSync() = runBlocking { selectNetwork() } - -fun IbmqProvider.selectNetworkSync( - picker: IbmqProvider.(NetworksResponse) -> Unit -) = runBlocking { selectNetwork(picker) } - -@JvmOverloads -fun IbmqProvider.selectDeviceSync( - simulator: Boolean = true, - minQubits: Int = 5 -) = runBlocking { selectDevice(simulator, minQubits) } - -fun IbmqProvider.selectDeviceSync( - picker: IbmqProvider.(BackendsResponse) -> Unit -) = runBlocking { selectDevice(picker) } - -fun IbmqProvider.getJobsSync() = runBlocking { getJobs() } - -fun IbmqProvider.getJobsLimitSync() = runBlocking { getJobsLimit() } - -fun IbmqProvider.runExperimentSync(request: RunExperimentRequest) = runBlocking { runExperiment(request) } - -@JvmOverloads -fun IbmqProvider.runExperimentSync( - qc: QuantumCircuit, - shots: Int = 1024, - name: String = "" -) = runBlocking { runExperiment(qc, shots, name) } - -fun IbmqProvider.updateExperimentSync( - codeId: String, - request: VersionsRequest -) = runBlocking { updateExperiment(codeId, request) } - -fun IbmqProvider.getExperimentSync(codeId: String) = runBlocking { getExperiment(codeId) } - -fun IbmqProvider.getExperimentsSync() = runBlocking { getExperiments() } - -fun IbmqProvider.newExperimentSync(request: NewRequest) = runBlocking { newExperiment(request) } - -fun IbmqProvider.getRunSync(jobId: String) = runBlocking { getRun(jobId) } - -fun IbmqProvider.getResultDownloadUrlSync(jobId: String) = runBlocking { getResultDownloadUrl(jobId) } - -fun IbmqProvider.getResultSync(url: String) = runBlocking { getResult(url) } - -fun IbmqProvider.andWaitSync( - timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION -) = runBlocking { andWait(timeoutDuration) } - -fun IbmqProvider.andWaitSync() = runBlocking { andWait() } - -fun IbmqProvider.runExperimentAndWaitSync( - request: RunExperimentRequest, - timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION -) = runBlocking { runExperimentAndWait(request, timeoutDuration) } - -fun IbmqProvider.runExperimentAndWaitSync(request: RunExperimentRequest) = runBlocking { runExperimentAndWait(request) } - -fun IbmqProvider.runExperimentAndWaitSync( - qc: QuantumCircuit, - timeoutDuration: Duration = IbmqProvider.JOB_TIMEOUT_DURATION -) = runBlocking { runExperimentAndWait(qc, timeoutDuration) } - -fun IbmqProvider.runExperimentAndWaitSync(qc: QuantumCircuit) = runBlocking { runExperimentAndWait(qc) } diff --git a/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt b/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt new file mode 100644 index 0000000..3a4b47f --- /dev/null +++ b/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApi.kt @@ -0,0 +1,61 @@ +package net.javaman.brackt.providers.ibmq.api + +import kotlinx.coroutines.runBlocking +import net.javaman.brackt.api.util.injections.injection +import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenRequest +import net.javaman.brackt.providers.ibmq.api.models.NewRequest +import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest +import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest + +@Suppress("TooManyFunctions") +actual class IbmqApi { + private val ibmqApiImpl: IbmqApiImpl by injection() + + fun logInWithTokenSync(request: LogInWithTokenRequest) = runBlocking { ibmqApiImpl.logInWithToken(request) } + + fun apiTokenSync(accessToken: String, userId: String) = runBlocking { ibmqApiImpl.apiToken(accessToken, userId) } + + fun networksSync(accessToken: String) = runBlocking { ibmqApiImpl.networks(accessToken) } + + fun backendsSync(accessToken: String) = runBlocking { ibmqApiImpl.backends(accessToken) } + + fun jobsSync(accessToken: String) = runBlocking { ibmqApiImpl.jobs(accessToken) } + + fun jobsLimitSync( + accessToken: String, + network: String, + group: String, + project: String, + device: String + ) = runBlocking { ibmqApiImpl.jobsLimit(accessToken, network, group, project, device) } + + fun runExperimentSync( + accessToken: String, + request: RunExperimentRequest, + network: String, + group: String, + project: String + ) = runBlocking { ibmqApiImpl.runExperiment(accessToken, request, network, group, project) } + + fun versionsSync( + accessToken: String, + request: VersionsRequest, + code: String + ) = runBlocking { ibmqApiImpl.versions(accessToken, request, code) } + + fun latestSync(accessToken: String, codeId: String) = runBlocking { ibmqApiImpl.latest(accessToken, codeId) } + + fun lastestSync(accessToken: String, userId: String) = runBlocking { ibmqApiImpl.lastest(accessToken, userId) } + + fun newCodeSync( + accessToken: String, + request: NewRequest + ) = runBlocking { ibmqApiImpl.newCode(accessToken, request) } + + fun jobSync(accessToken: String, jobId: String) = runBlocking { ibmqApiImpl.job(accessToken, jobId) } + + fun resultDownloadUrlSync( + accessToken: String, + jobId: String + ) = runBlocking { ibmqApiImpl.resultDownloadUrl(accessToken, jobId) } +} diff --git a/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiPlatform.kt b/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiPlatform.kt deleted file mode 100644 index b5ea8f3..0000000 --- a/brac-kt-ibmq-provider/src/jvmMain/kotlin/net/javaman/brackt/providers/ibmq/api/IbmqApiPlatform.kt +++ /dev/null @@ -1,52 +0,0 @@ -package net.javaman.brackt.providers.ibmq.api - -import kotlinx.coroutines.runBlocking -import net.javaman.brackt.providers.ibmq.api.models.LogInWithTokenRequest -import net.javaman.brackt.providers.ibmq.api.models.NewRequest -import net.javaman.brackt.providers.ibmq.api.models.RunExperimentRequest -import net.javaman.brackt.providers.ibmq.api.models.VersionsRequest - -fun IbmqApi.logInWithTokenSync(request: LogInWithTokenRequest) = runBlocking { logInWithToken(request) } - -fun IbmqApi.apiTokenSync(accessToken: String, userId: String) = runBlocking { apiToken(accessToken, userId) } - -fun IbmqApi.networksSync(accessToken: String) = runBlocking { networks(accessToken) } - -fun IbmqApi.backendsSync(accessToken: String) = runBlocking { backends(accessToken) } - -fun IbmqApi.jobsSync(accessToken: String) = runBlocking { jobs(accessToken) } - -fun IbmqApi.jobsLimitSync( - accessToken: String, - network: String, - group: String, - project: String, - device: String -) = runBlocking { jobsLimit(accessToken, network, group, project, device) } - -fun IbmqApi.runExperimentSync( - accessToken: String, - request: RunExperimentRequest, - network: String, - group: String, - project: String -) = runBlocking { runExperiment(accessToken, request, network, group, project) } - -fun IbmqApi.versionsSync( - accessToken: String, - request: VersionsRequest, - code: String -) = runBlocking { versions(accessToken, request, code) } - -fun IbmqApi.latestSync(accessToken: String, codeId: String) = runBlocking { latest(accessToken, codeId) } - -fun IbmqApi.lastestSync(accessToken: String, userId: String) = runBlocking { lastest(accessToken, userId) } - -fun IbmqApi.newCodeSync(accessToken: String, request: NewRequest) = runBlocking { newCode(accessToken, request) } - -fun IbmqApi.jobSync(accessToken: String, jobId: String) = runBlocking { job(accessToken, jobId) } - -fun IbmqApi.resultDownloadUrlSync( - accessToken: String, - jobId: String -) = runBlocking { resultDownloadUrl(accessToken, jobId) } diff --git a/build.gradle.kts b/build.gradle.kts index e1ec01f..8d3ad63 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,9 +14,7 @@ repositories { subprojects { group = "net.javaman.brac-kt" - version = "0.1.4" - - apply(plugin = "io.gitlab.arturbosch.detekt") + version = "0.1.5" ext { set("jvmTarget", "11") diff --git a/example-js-kotlin/build.gradle.kts b/example-js-kotlin/build.gradle.kts index 9f4a1ea..afb235b 100644 --- a/example-js-kotlin/build.gradle.kts +++ b/example-js-kotlin/build.gradle.kts @@ -1,5 +1,6 @@ plugins { kotlin("js") + id("io.gitlab.arturbosch.detekt") } repositories { @@ -7,7 +8,6 @@ repositories { } dependencies { - implementation(project(":brac-kt-api")) implementation(project(":brac-kt-ibmq-provider")) } diff --git a/example-js-kotlin/src/main/kotlin/App.kt b/example-js-kotlin/src/main/kotlin/App.kt index fbdbf25..797cab4 100644 --- a/example-js-kotlin/src/main/kotlin/App.kt +++ b/example-js-kotlin/src/main/kotlin/App.kt @@ -4,7 +4,7 @@ import net.javaman.brackt.api.BracKtApi import net.javaman.brackt.api.quantum.QuantumCircuit import net.javaman.brackt.api.util.injections.injection import net.javaman.brackt.api.util.properties.PropertyManager -import net.javaman.brackt.providers.ibmq.IbmqProvider +import net.javaman.brackt.providers.ibmq.IbmqProviderImpl import net.javaman.brackt.providers.ibmq.logIn import net.javaman.brackt.providers.ibmq.runExperimentAndWait import net.javaman.brackt.providers.ibmq.selectDevice @@ -15,11 +15,11 @@ suspend fun main() = App.run() object App { // Dependencies are managed by InjectionManager private val propertyManager: PropertyManager by injection() - private val ibmqProvider: IbmqProvider by injection() + private val ibmqProvider: IbmqProviderImpl by injection() init { BracKtApi.addInjections() - IbmqProvider.addInjections() + IbmqProviderImpl.addInjections() } suspend fun run() { // suspend keyword allows using Kotlin's (speedy) coroutines diff --git a/example-js-typescript/package-lock.json b/example-js-typescript/package-lock.json index 9bc7180..2125263 100644 --- a/example-js-typescript/package-lock.json +++ b/example-js-typescript/package-lock.json @@ -9,7 +9,6 @@ "version": "0.1.4", "dependencies": { "@js-joda/core": "^4.3.1", - "@thenewjavaman/brac-kt-api": "^0.1.4", "@thenewjavaman/brac-kt-ibmq-provider": "^0.1.4", "eslint": "^8.4.1", "typescript": "^4.5.2" @@ -60,11 +59,6 @@ "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-4.3.1.tgz", "integrity": "sha512-oeaetlodcqVsiZDxnEcqsbs+sXBkASxua0mXs5OXuPQXz3/wdPTMlxwfQ4z2HKcOik3S9voW3QJkp/KLWDhvRQ==" }, - "node_modules/@thenewjavaman/brac-kt-api": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@thenewjavaman/brac-kt-api/-/brac-kt-api-0.1.4.tgz", - "integrity": "sha512-nShk6RFpU00Y8h0ZPFDOvbVymuEJqLATCoy3ptcKh/jt9fCOJI8LyFR5GBVn6Pzz2N+B+r403SoBQT5NG+crPA==" - }, "node_modules/@thenewjavaman/brac-kt-ibmq-provider": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/@thenewjavaman/brac-kt-ibmq-provider/-/brac-kt-ibmq-provider-0.1.4.tgz", @@ -960,11 +954,6 @@ "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-4.3.1.tgz", "integrity": "sha512-oeaetlodcqVsiZDxnEcqsbs+sXBkASxua0mXs5OXuPQXz3/wdPTMlxwfQ4z2HKcOik3S9voW3QJkp/KLWDhvRQ==" }, - "@thenewjavaman/brac-kt-api": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@thenewjavaman/brac-kt-api/-/brac-kt-api-0.1.4.tgz", - "integrity": "sha512-nShk6RFpU00Y8h0ZPFDOvbVymuEJqLATCoy3ptcKh/jt9fCOJI8LyFR5GBVn6Pzz2N+B+r403SoBQT5NG+crPA==" - }, "@thenewjavaman/brac-kt-ibmq-provider": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/@thenewjavaman/brac-kt-ibmq-provider/-/brac-kt-ibmq-provider-0.1.4.tgz", diff --git a/example-js-typescript/package.json b/example-js-typescript/package.json index bd6c912..bf68c56 100644 --- a/example-js-typescript/package.json +++ b/example-js-typescript/package.json @@ -1,18 +1,20 @@ { - "name": "example-js-typescript", - "version": "0.1.4", + "name": "@thenewjavaman/example-js-typescript", + "version": "0.1.5", "description": "A Kotlin/Multiplatform interface for quantum computing", - "main": "app.js", + "main": "dist/app.js", "author": "Gabriel Pizarro", "dependencies": { "@js-joda/core": "^4.3.1", - "@thenewjavaman/brac-kt-api": "^0.1.4", - "@thenewjavaman/brac-kt-ibmq-provider": "^0.1.4", "eslint": "^8.4.1", "typescript": "^4.5.2" }, "scripts": { - "start": "tsc && node dist/app.js" + "start": "node .", + "build": "tsc", + "buildProject": "cd .. && .\\gradlew jsProductionExecutableCompileSync && cd example-js-typescript", + "buildAndStart": "npm run build && npm start", + "buildAllAndStart": "npm run buildProject && npm run buildAndStart" }, "devDependencies": { "@types/node": "^16.11.11" diff --git a/example-js-typescript/src/app.ts b/example-js-typescript/src/app.ts index f9866f9..39beb0d 100644 --- a/example-js-typescript/src/app.ts +++ b/example-js-typescript/src/app.ts @@ -1,10 +1,31 @@ +// Imports require('@js-joda/core'); -const bracKtApi = require('@thenewjavaman/brac-kt-api').net.javaman.brackt.api; -const bracKtIbmqProvider = require('@thenewjavaman/brac-kt-ibmq-provider').net.javaman.brackt.providers.ibmq; +const bracKt = require('../../build/js/packages/brac-kt-ibmq-provider/kotlin/brac-kt-ibmq-provider').net.javaman.brackt -const n = 3; -const qc = new bracKtApi.quantum.QuantumCircuit("Example Superposition", 3); -for (let i = 0; i < n; i++) qc.h(i); -for (let i = 0; i < n; i++) qc.measure(i, i); +// Type definitions +const Logger = bracKt.api.util.logging.Logger; +const QuantumCircuit = bracKt.api.quantum.QuantumCircuit; +const IbmqProvider = bracKt.providers.ibmq.IbmqProvider; -console.log(qc); +const run = async () => { + bracKt.api.addInjections(); + bracKt.providers.ibmq.addInjections(); + + // New quantum circuit + const n = 3; + const qc = new QuantumCircuit("Example Superposition", 3); + for (let i = 0; i < n; i++) qc.h(i); + for (let i = 0; i < n; i++) qc.measure(i, i); + + // Run the circuit on an IBM simulator + const ibmqProvider = new IbmqProvider(); + await ibmqProvider.logInAsync(process.env.IBMQ_API_TOKEN); + await ibmqProvider.selectNetworkAsync(); + await ibmqProvider.selectDeviceAsync(); + await ibmqProvider.runExperimentAndWaitAsync(qc); +}; + +run().then(() => { + const logger = new Logger("app"); + logger.info(() => "Run complete"); +}); diff --git a/example-js-typescript/tsconfig.json b/example-js-typescript/tsconfig.json index 43c5fc7..e438b84 100644 --- a/example-js-typescript/tsconfig.json +++ b/example-js-typescript/tsconfig.json @@ -11,7 +11,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016", + "target": "es2017", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ diff --git a/example-jvm-java/build.gradle.kts b/example-jvm-java/build.gradle.kts index 9482b47..755f52a 100644 --- a/example-jvm-java/build.gradle.kts +++ b/example-jvm-java/build.gradle.kts @@ -7,7 +7,6 @@ repositories { } dependencies { - implementation(project(":brac-kt-api")) implementation(project(":brac-kt-ibmq-provider")) } diff --git a/example-jvm-java/src/main/java/Application.java b/example-jvm-java/src/main/java/Application.java index 3b441d3..d9e0ae5 100644 --- a/example-jvm-java/src/main/java/Application.java +++ b/example-jvm-java/src/main/java/Application.java @@ -1,14 +1,14 @@ import net.javaman.brackt.api.BracKtApi; import net.javaman.brackt.api.quantum.QuantumCircuit; import net.javaman.brackt.providers.ibmq.IbmqProvider; -import net.javaman.brackt.providers.ibmq.IbmqProviderPlatformKt; +import net.javaman.brackt.providers.ibmq.IbmqProviderImpl; public class Application { private static final IbmqProvider ibmqProvider = new IbmqProvider(); static { BracKtApi.addInjections(); - IbmqProvider.addInjections(); + IbmqProviderImpl.addInjections(); } public static void main(String[] args) { @@ -20,9 +20,9 @@ public static void main(String[] args) { // Let Kotlin handle the coroutines String apiToken = System.getenv("IBMQ_API_TOKEN"); - IbmqProviderPlatformKt.logInSync(ibmqProvider, apiToken); - IbmqProviderPlatformKt.selectNetworkSync(ibmqProvider); - IbmqProviderPlatformKt.selectDeviceSync(ibmqProvider); - IbmqProviderPlatformKt.runExperimentAndWaitSync(ibmqProvider, qc); + ibmqProvider.logInSync(apiToken); + ibmqProvider.selectNetworkSync(); + ibmqProvider.selectDeviceSync(); + ibmqProvider.runExperimentAndWaitSync(qc); } } diff --git a/example-jvm-kotlin/build.gradle.kts b/example-jvm-kotlin/build.gradle.kts index fb162ec..ef9353b 100644 --- a/example-jvm-kotlin/build.gradle.kts +++ b/example-jvm-kotlin/build.gradle.kts @@ -1,6 +1,7 @@ plugins { kotlin("jvm") application + id("io.gitlab.arturbosch.detekt") } repositories { @@ -8,7 +9,6 @@ repositories { } dependencies { - implementation(project(":brac-kt-api")) implementation(project(":brac-kt-ibmq-provider")) } diff --git a/example-jvm-kotlin/src/main/kotlin/Application.kt b/example-jvm-kotlin/src/main/kotlin/Application.kt index 1bcecb3..0f7d0a0 100644 --- a/example-jvm-kotlin/src/main/kotlin/Application.kt +++ b/example-jvm-kotlin/src/main/kotlin/Application.kt @@ -5,7 +5,7 @@ import net.javaman.brackt.api.BracKtApi import net.javaman.brackt.api.quantum.QuantumCircuit import net.javaman.brackt.api.util.injections.injection import net.javaman.brackt.api.util.properties.PropertyManager -import net.javaman.brackt.providers.ibmq.IbmqProvider +import net.javaman.brackt.providers.ibmq.IbmqProviderImpl import net.javaman.brackt.providers.ibmq.logIn import net.javaman.brackt.providers.ibmq.runExperimentAndWait import net.javaman.brackt.providers.ibmq.selectDevice @@ -14,11 +14,11 @@ import net.javaman.brackt.providers.ibmq.selectNetwork object Application { // Dependencies are managed by InjectionManager private val propertyManager: PropertyManager by injection() - private val ibmqProvider: IbmqProvider by injection() + private val ibmqProvider: IbmqProviderImpl by injection() init { BracKtApi.addInjections() - IbmqProvider.addInjections() + IbmqProviderImpl.addInjections() } @JvmStatic