Skip to content

Commit

Permalink
feat: Add Kipu Qubo Solver
Browse files Browse the repository at this point in the history
  • Loading branch information
Elscrux committed Nov 25, 2024
1 parent 74d8299 commit 16b59f5
Show file tree
Hide file tree
Showing 2 changed files with 326 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import edu.kit.provideq.toolbox.meta.ProblemManager;
import edu.kit.provideq.toolbox.meta.ProblemType;
import edu.kit.provideq.toolbox.qubo.solvers.DwaveQuboSolver;
import edu.kit.provideq.toolbox.qubo.solvers.KipuQuboSolver;
import edu.kit.provideq.toolbox.qubo.solvers.QiskitQuboSolver;
import edu.kit.provideq.toolbox.qubo.solvers.QrispQuboSolver;
import edu.kit.provideq.toolbox.qubo.solvers.QuantagoniaQuboSolver;
Expand Down Expand Up @@ -38,11 +39,12 @@ ProblemManager<String, String> getQuboManager(
DwaveQuboSolver dwaveSolver,
QrispQuboSolver qrispSolver,
QuantagoniaQuboSolver quantagoniaQuboSolver,
KipuQuboSolver kipuQuboSolver,
ResourceProvider resourceProvider
) {
return new ProblemManager<>(
QUBO,
Set.of(qiskitSolver, dwaveSolver, qrispSolver, quantagoniaQuboSolver),
Set.of(qiskitSolver, dwaveSolver, qrispSolver, quantagoniaQuboSolver, kipuQuboSolver),
loadExampleProblems(resourceProvider)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
package edu.kit.provideq.toolbox.qubo.solvers;

import com.fasterxml.jackson.annotation.JsonProperty;
import de.asbestian.jplex.input.LpFileReader;
import de.asbestian.jplex.input.Variable;
import edu.kit.provideq.toolbox.Solution;
import edu.kit.provideq.toolbox.exception.ConversionException;
import edu.kit.provideq.toolbox.integration.planqk.PlanQkApi;
import edu.kit.provideq.toolbox.integration.planqk.PlanQkApi.JobInfo;
import edu.kit.provideq.toolbox.integration.planqk.PlanQkApi.ProblemProperties;
import edu.kit.provideq.toolbox.integration.planqk.PlanQkApi.ResultProperties;
import edu.kit.provideq.toolbox.integration.planqk.PlanQkApi.StatusProperties;
import edu.kit.provideq.toolbox.meta.SolvingProperties;
import edu.kit.provideq.toolbox.meta.SubRoutineResolver;
import edu.kit.provideq.toolbox.meta.setting.SolverSetting;
import edu.kit.provideq.toolbox.meta.setting.basic.TextSetting;
import edu.kit.provideq.toolbox.qubo.QuboConfiguration;
import java.io.BufferedReader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.ToIntFunction;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
* {@link QuboConfiguration#QUBO} solver using the
* Kipu Digitized Counterdiabatic Quantum Optimization (DCQO) solver hosted on the PlanQK platform.
*/
@Component
public class KipuQuboSolver extends QuboSolver {
private static final String SETTING_PLANQK_TOKEN = "PlanQK Access Token";

@Override
public String getName() {
return "(PlanQK) Kipu QUBO Solver";
}

@Override
public List<SolverSetting> getSolverSettings() {
return List.of(
new TextSetting(
SETTING_PLANQK_TOKEN,
"Create access token as shown in docs: https://docs.planqk.de/services/applications.html"
)
);
}

@Override
public Mono<Solution<String>> solve(
String input,
SubRoutineResolver subRoutineResolver,
SolvingProperties properties) {
Optional<String> planQkToken = properties
.<TextSetting>getSetting(SETTING_PLANQK_TOKEN)
.map(TextSetting::getText);

// Return if no token is provided
if (planQkToken.isEmpty()) {
var solution = new Solution<>(this);
solution.setDebugData("No PlanQK token provided.");
solution.abort();
return Mono.just(solution);
}

// Convert problem data string to buffered reader
var problemDataReader = new BufferedReader(new StringReader(input));
// Parse lp data
LpFileReader lpReader = new LpFileReader(problemDataReader);

KipuQuboProblem kipuQubo;
try {
kipuQubo = parseKipuQubo(lpReader);
} catch (ConversionException e) {
var solution = new Solution<>(this);
solution.setDebugData(e.getMessage());
solution.abort();
return Mono.just(solution);
}

var kipuQuboRequest = new KipuQuboRequest();
kipuQuboRequest.getData().setOptimization(kipuQubo);

PlanQkApi api = new PlanQkApi();
return api.call(
"/kipu-quantum/kipu-digitized-counterdiabatic-quantum-optimization---dcqo/1.0.0",
kipuQuboRequest,
planQkToken.get(),
new ProblemProperties<>("/", JobInfo.class, JobInfo::getId),
new StatusProperties<>("/%s", JobInfo.class, JobInfo::getStatus),
new ResultProperties<>("/%s/result", KipuQuboSolution.class)
).flatMap(result -> {
var solution = new Solution<>(this);
if (result == null) {
solution.setDebugData("Job couldn't be solved.");
solution.abort();
return Mono.just(solution);
}

var entryStrings = result.counts.entrySet()
.stream()
.map(x -> x.getKey() + ": " + x.getValue())
.toList();

solution.setSolutionData(String.join("\n", entryStrings));
solution.complete();
return Mono.just(solution);
});
}

private static KipuQuboProblem parseKipuQubo(LpFileReader lpReader)
throws ConversionException {
var qubo = new KipuQuboProblem();

var binaryVariables = lpReader.getBinaryVariables();
ToIntFunction<Variable> getVariableIndex = binaryVariables::indexOf;

// Fill matrix
var objective = lpReader.getObjective(0);
for (var term : objective.terms()) {
var multiplicands = term.multiplicands();
if (multiplicands.size() != 2) {
throw new ConversionException("Only quadratic terms are supported");
}

var var1 = getVariableIndex.applyAsInt(multiplicands.get(0));
var var2 = getVariableIndex.applyAsInt(multiplicands.get(1));
qubo.getCoefficients().put(
var1 == var2
? String.format("(%d,)", var1)
: String.format("(%d, %d)", var1, var2),
term.coefficient()
);
}

return qubo;
}

static class KipuQuboRequest {
@JsonProperty("data")
private KipuQuboData data = new KipuQuboData();

@JsonProperty("params")
private KipuQuboParams params = new KipuQuboParams();

public KipuQuboData getData() {
return data;
}

public void setData(
KipuQuboData data) {
this.data = data;
}

public KipuQuboParams getParams() {
return params;
}

public void setParams(
KipuQuboParams params) {
this.params = params;
}

static class KipuQuboData {
@JsonProperty("optimization")
private KipuQuboProblem optimization;

public KipuQuboProblem getOptimization() {
return optimization;
}

public void setOptimization(
KipuQuboProblem optimization) {
this.optimization = optimization;
}
}

static class KipuQuboParams {
@JsonProperty("backend")
private String backend = "azure.ionq.simulator";

@JsonProperty("shots")
private int shots = 1024;

public String getBackend() {
return backend;
}

public void setBackend(String backend) {
this.backend = backend;
}

public int getShots() {
return shots;
}

public void setShots(int shots) {
this.shots = shots;
}
}
}

static class KipuQuboProblem {
@JsonProperty("coefficients")
private Map<String, Double> coefficients = new HashMap<>();

@JsonProperty("annealing_time")
private double annealingTime = 0.7;

@JsonProperty("trotter_steps")
private int trotterSteps = 2;

@JsonProperty("mode")
private Mode mode = Mode.CD;

public Map<String, Double> getCoefficients() {
return coefficients;
}

public void setCoefficients(Map<String, Double> coefficients) {
this.coefficients = coefficients;
}

public double getAnnealingTime() {
return annealingTime;
}

public void setAnnealingTime(Double annealingTime) {
this.annealingTime = annealingTime;
}

public int getTrotterSteps() {
return trotterSteps;
}

public void setTrotterSteps(int trotterSteps) {
this.trotterSteps = trotterSteps;
}

public Mode getMode() {
return mode;
}

public void setMode(
Mode mode) {
this.mode = mode;
}

enum Mode {
CD,
FULL
}
}

static class KipuQuboError {
private String code;
private String detail;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getDetail() {
return detail;
}

public void setDetail(String detail) {
this.detail = detail;
}
}

static class KipuQuboSolution {
@JsonProperty("job_id")
private String jobId;

@JsonProperty("backend_name")
private String backendName;

@JsonProperty("counts")
private Map<String, Integer> counts;

@JsonProperty("shots")
private int shots;

public String getJobId() {
return jobId;
}

public void setJobId(String jobId) {
this.jobId = jobId;
}

public String getBackendName() {
return backendName;
}

public void setBackendName(String backendName) {
this.backendName = backendName;
}

public Map<String, Integer> getCounts() {
return counts;
}

public void setCounts(Map<String, Integer> counts) {
this.counts = counts;
}

public int getShots() {
return shots;
}

public void setShots(int shots) {
this.shots = shots;
}
}
}

0 comments on commit 16b59f5

Please sign in to comment.