diff --git a/build.gradle b/build.gradle index 7c999150..393fa132 100644 --- a/build.gradle +++ b/build.gradle @@ -7,11 +7,15 @@ plugins { group = 'edu.kit.provideq' version = '0.4.0' -sourceCompatibility = '17' + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} repositories { mavenCentral() - maven { url 'https://jitpack.io' } + maven { url = 'https://jitpack.io' } } dependencies { @@ -35,9 +39,6 @@ tasks.named('test') { useJUnitPlatform() } -// set Java language level -targetCompatibility = JavaVersion.VERSION_17 - tasks.withType(JavaCompile).configureEach { // treat "unchecked" warnings as errors options.compilerArgs << '-Xlint:unchecked' diff --git a/demonstrators/qiskit/molecule-energy/molecule-energy.py b/demonstrators/qiskit/molecule-energy/molecule-energy.py new file mode 100644 index 00000000..f006e654 --- /dev/null +++ b/demonstrators/qiskit/molecule-energy/molecule-energy.py @@ -0,0 +1,113 @@ +from qiskit import QuantumCircuit +from qiskit_algorithms import VQE +from qiskit_algorithms.optimizers import L_BFGS_B, SLSQP +from qiskit.circuit.library import TwoLocal +from qiskit_nature.second_q.algorithms import GroundStateEigensolver +from qiskit_nature.second_q.circuit.library import HartreeFock, UCCSD +from qiskit_nature.second_q.drivers import PySCFDriver +from qiskit_nature.second_q.mappers import JordanWignerMapper, ParityMapper +from qiskit.primitives import Estimator +import matplotlib.pyplot as plt +from io import StringIO +import numpy as np +import sys + +arg_count = len(sys.argv) - 1 +if arg_count != 1: + raise ValueError(f'This script expects exactly 1 argument: the molecule, but got {arg_count}: {sys.argv[1:]}') +molecule = sys.argv[1] + +driver = PySCFDriver(atom=molecule) +problem = driver.run() + +mapper = ParityMapper(num_particles=problem.num_particles) + +optimizer = L_BFGS_B() + +estimator = Estimator() + +ansatz = UCCSD( + problem.num_spatial_orbitals, + problem.num_particles, + mapper, + initial_state=HartreeFock( + problem.num_spatial_orbitals, + problem.num_particles, + mapper, + ), +) + +vqe = VQE(estimator, ansatz, optimizer) +vqe.initial_point = [0] * ansatz.num_parameters + +algorithm = GroundStateEigensolver(mapper, vqe) + +electronic_structure_result = algorithm.solve(problem) +electronic_structure_result.formatting_precision = 6 + +driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.74279') +problem = driver.run() + +mapper = JordanWignerMapper() + +optimizer = SLSQP(maxiter=10000, ftol=1e-9) + +estimator = Estimator() + +var_forms = [['ry', 'rz'], 'ry'] +entanglements = ['full', 'linear'] +entanglement_blocks = ['cx', 'cz', ['cx', 'cz']] +depths = list(range(1, 11)) + +reference_circuit = QuantumCircuit(4) +reference_circuit.x(0) +reference_circuit.x(2) + +results = np.zeros((len(depths), len(entanglements), len(var_forms), len(entanglement_blocks))) + +for i, d in enumerate(depths): + for j, e in enumerate(entanglements): + for k, vf in enumerate(var_forms): + for l, eb in enumerate(entanglement_blocks): + variational_form = TwoLocal(4, rotation_blocks=vf, entanglement_blocks=eb, entanglement=e, reps=d) + + ansatz = reference_circuit.compose(variational_form) + + vqe = VQE(estimator, ansatz, optimizer) + vqe.initial_point = [0] * ansatz.num_parameters + + algorithm = GroundStateEigensolver(mapper, vqe) + + electronic_structure_result = algorithm.solve(problem) + + results[i, j, k, l] = electronic_structure_result.total_energies[0] + +fig1, axs1 = plt.subplots(2, 3, sharey=True, sharex=True) +fig2, axs2 = plt.subplots(2, 3, sharey=True, sharex=True) + +fig1.supxlabel('Depth') +fig1.supylabel('Estimated ground state energy') +fig2.supxlabel('Depth') +fig2.supylabel('Estimated ground state energy') + +for j, e in enumerate(entanglements): + for l, eb in enumerate(entanglement_blocks): + axs1[j, l].plot(depths, results[:, j, 0, l]) + axs1[j, l].set_title(f'{e}, ryrz, {eb}') + axs1[j, l].text(0.90, 0.75, f'Min: {np.min(results[:, j, 0, l]):.3f}') + +for j, e in enumerate(entanglements): + for l, eb in enumerate(entanglement_blocks): + axs2[j, l].plot(depths, results[:, j, 1, l]) + axs2[j, l].set_title(f'{e}, ry, {eb}') + axs2[j, l].text(0.90, 0.75, f'Min: {np.min(results[:, j, 1, l]):.3f}') + + +def print_fig(fig): + string_io = StringIO() + fig.savefig(string_io, format='svg') + print(string_io.getvalue()) + + +print_fig(fig1) +print_fig(fig2) diff --git a/demonstrators/qiskit/molecule-energy/requirements.txt b/demonstrators/qiskit/molecule-energy/requirements.txt new file mode 100644 index 00000000..b14633c4 --- /dev/null +++ b/demonstrators/qiskit/molecule-energy/requirements.txt @@ -0,0 +1,14 @@ +# This file describes the python package requirements for all Qiskit demonstrator scripts +# supported by ProvideQ + +# required for molecule energy solver +qiskit==1.1.0 +qiskit-aer==0.14.2 +qiskit-algorithms==0.3.0 +qiskit-ibm-runtime==0.25.0 +qiskit-machine-learning==0.7.2 +qiskit-nature==0.7.2 +qiskit-nature-pyscf==0.4.0 +qiskit-qasm3-import==0.5.0 +qiskit-transpiler-service==0.4.5 +matplotlib==3.9.2 \ No newline at end of file diff --git a/solvers/custom/hs-knapsack/knapsack.py b/solvers/custom/hs-knapsack/knapsack.py index e1863aa8..5d76a53f 100644 --- a/solvers/custom/hs-knapsack/knapsack.py +++ b/solvers/custom/hs-knapsack/knapsack.py @@ -4,7 +4,7 @@ from knapsack01 import HSKnapsack if len(sys.argv) != 3: - raise TypeError('This script expects exactly 2 arguments. Input file (argument 1) and output file (argument 2).') + raise ValueError('This script expects exactly 2 arguments. Input file (argument 1) and output file (argument 2).') input_path = sys.argv[1] output_path = sys.argv[2] diff --git a/solvers/qiskit/max-cut/maxCut_qiskit.py b/solvers/qiskit/max-cut/maxCut_qiskit.py index 2cf67fc4..b921345d 100644 --- a/solvers/qiskit/max-cut/maxCut_qiskit.py +++ b/solvers/qiskit/max-cut/maxCut_qiskit.py @@ -23,7 +23,7 @@ #if len(sys.argv) != 3: -# raise TypeError('This script expects exactly 2 arguments. Input file (argument 1) and output file (argument 2).') +# raise ValueError('This script expects exactly 2 arguments. Input file (argument 1) and output file (argument 2).') input_path = sys.argv[1] output_path = sys.argv[2] diff --git a/solvers/qiskit/qubo/qubo_qiskit.py b/solvers/qiskit/qubo/qubo_qiskit.py index d7b3469d..8a9b03fd 100644 --- a/solvers/qiskit/qubo/qubo_qiskit.py +++ b/solvers/qiskit/qubo/qubo_qiskit.py @@ -7,7 +7,7 @@ from qiskit_optimization.algorithms import MinimumEigenOptimizer if len(sys.argv) != 3: - raise TypeError('This script expects exactly 2 arguments. Input file (argument 1) and output file (argument 2).') + raise ValueError('This script expects exactly 2 arguments. Input file (argument 1) and output file (argument 2).') input_path = sys.argv[1] output_path = sys.argv[2] diff --git a/src/main/java/edu/kit/provideq/toolbox/demonstrators/DemonstratorConfiguration.java b/src/main/java/edu/kit/provideq/toolbox/demonstrators/DemonstratorConfiguration.java index 5767fe52..5d380922 100644 --- a/src/main/java/edu/kit/provideq/toolbox/demonstrators/DemonstratorConfiguration.java +++ b/src/main/java/edu/kit/provideq/toolbox/demonstrators/DemonstratorConfiguration.java @@ -21,10 +21,11 @@ public class DemonstratorConfiguration { @Bean ProblemManager getDemonstratorManager( - CplexMipDemonstrator cplexMipDemonstrator + CplexMipDemonstrator cplexMipDemonstrator, + MoleculeEnergySimulator moleculeEnergySimulator ) { return new ProblemManager<>(DEMONSTRATOR, - Set.of(cplexMipDemonstrator), + Set.of(cplexMipDemonstrator, moleculeEnergySimulator), Set.of(new Problem<>(DEMONSTRATOR))); } } diff --git a/src/main/java/edu/kit/provideq/toolbox/demonstrators/MoleculeEnergySimulator.java b/src/main/java/edu/kit/provideq/toolbox/demonstrators/MoleculeEnergySimulator.java new file mode 100644 index 00000000..2210bcb2 --- /dev/null +++ b/src/main/java/edu/kit/provideq/toolbox/demonstrators/MoleculeEnergySimulator.java @@ -0,0 +1,79 @@ +package edu.kit.provideq.toolbox.demonstrators; + +import edu.kit.provideq.toolbox.Solution; +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.process.PythonProcessRunner; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +/** + * Demonstrator for the Molecule Energy simulation. + * Note that its python dependencies can only be installed on Linux and macOS. + * Based on this Jupyter Notebook + */ +@Component +public class MoleculeEnergySimulator implements Demonstrator { + private final String scriptPath; + private final String venv; + private final ApplicationContext context; + + private static final String SETTING_MOLECULE = "Molecule"; + private static final String DEFAULT_MOLECULE = "H .0 .0 .0; H .0 .0 0.74279"; + + @Autowired + public MoleculeEnergySimulator( + @Value("${path.demonstrators.qiskit.molecule-energy}") String scriptPath, + @Value("${venv.demonstrators.qiskit.molecule-energy}") String venv, + ApplicationContext context) { + this.scriptPath = scriptPath; + this.venv = venv; + this.context = context; + } + + @Override + public String getName() { + return "Molecule Energy Simulator"; + } + + @Override + public String getDescription() { + return "Computes the ground state energy for a given molecule using VQE algorithm."; + } + + @Override + public List getSolverSettings() { + return List.of( + new TextSetting( + false, + SETTING_MOLECULE, + "The molecule to be simulated in XYZ format - a di-hydrogen molecule by default", + DEFAULT_MOLECULE + ) + ); + } + + @Override + public Mono> solve(String input, SubRoutineResolver subRoutineResolver, + SolvingProperties properties) { + var solution = new Solution<>(this); + + var molecule = properties.getSetting(SETTING_MOLECULE) + .map(TextSetting::getText) + .orElse(DEFAULT_MOLECULE); + + var processResult = context + .getBean(PythonProcessRunner.class, scriptPath, venv) + .withArguments('"' + molecule + '"') + .readOutputString() + .run(getProblemType(), solution.getId()); + + return Mono.just(processResult.applyTo(solution)); + } +} diff --git a/src/main/java/edu/kit/provideq/toolbox/process/ProcessRunner.java b/src/main/java/edu/kit/provideq/toolbox/process/ProcessRunner.java index 21314223..c31bb7ed 100644 --- a/src/main/java/edu/kit/provideq/toolbox/process/ProcessRunner.java +++ b/src/main/java/edu/kit/provideq/toolbox/process/ProcessRunner.java @@ -294,8 +294,6 @@ protected ProcessRunnerExecutor getExecutor( int processExitCode; try { processBuilder.directory(new File(System.getProperty("user.dir"))); - String command = - processBuilder.command().stream().reduce("", (a, b) -> a + " |break| " + b); Process process = processBuilder.start(); processOutput = resourceProvider.readStream(process.inputReader()); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 86d54fb5..fb472f59 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1 @@ -# default spring profile, correct one will be set during runtime (see ToolboxServerApplication.java) # options: mac, windows, linux spring.profiles.active=linux springdoc.swagger-ui.operationsSorter=alpha springdoc.swagger-ui.tagsSorter=alpha working.directory=jobs examples.directory=examples springdoc.swagger-ui.path=/ # Solvers name.solvers=solvers # Non OS-specific solvers: (typically GAMS and Python) name.gams=gams path.gams=${name.solvers}/${name.gams} name.gams.max-cut=max-cut path.gams.max-cut=${path.gams}/${name.gams.max-cut}/maxcut.gms name.gams.sat=sat path.gams.sat=${path.gams}/${name.gams.sat}/sat.gms name.qiskit=qiskit path.qiskit=${name.solvers}/${name.qiskit} name.qiskit.knapsack=knapsack path.qiskit.knapsack=${path.qiskit}/${name.qiskit.knapsack}/knapsack_qiskit.py venv.qiskit.knapsack=${name.solvers}_${name.qiskit}_${name.qiskit.knapsack} name.qiskit.max-cut=max-cut path.qiskit.max-cut=${path.qiskit}/${name.qiskit.max-cut}/maxCut_qiskit.py venv.qiskit.max-cut=${name.solvers}_${name.qiskit}_${name.qiskit.max-cut} name.qiskit.qubo=qubo path.qiskit.qubo=${path.qiskit}/${name.qiskit.qubo}/qubo_qiskit.py venv.qiskit.qubo=${name.solvers}_${name.qiskit}_${name.qiskit.qubo} name.cirq=cirq path.cirq=${name.solvers}/${name.cirq} name.cirq.max-cut=max-cut path.cirq.max-cut=${path.cirq}/${name.cirq.max-cut}/max_cut_cirq.py venv.cirq.max-cut=${name.solvers}_${name.cirq}_${name.cirq.max-cut} name.qrisp=qrisp path.qrisp=${name.solvers}/${name.qrisp} name.qrisp.vrp=vrp path.qrisp.vrp=${path.qrisp}/${name.qrisp.vrp}/grover.py venv.qrisp.vrp=${name.solvers}_${name.qrisp}_${name.qrisp.vrp} name.qrisp.qubo=qubo path.qrisp.qubo=${path.qrisp}/${name.qrisp.qubo}/qaoa.py venv.qrisp.qubo=${name.solvers}_${name.qrisp}_${name.qrisp.qubo} name.qrisp.sat=sat path.qrisp.sat.grover=${path.qrisp}/${name.qrisp.sat}/grover.py path.qrisp.sat.exact=${path.qrisp}/${name.qrisp.sat}/exact_grover.py venv.qrisp.sat=${name.solvers}_${name.qrisp}_${name.qrisp.sat} name.dwave=dwave path.dwave=${name.solvers}/${name.dwave} name.dwave.qubo=qubo path.dwave.qubo=${path.dwave}/${name.dwave.qubo}/main.py venv.dwave.qubo=${name.solvers}_${name.dwave}_${name.dwave.qubo} # Non OS-specific custom solvers: (solvers that are not part of a framework) name.custom=custom path.custom=${name.solvers}/${name.custom} name.custom.hs-knapsack=hs-knapsack path.custom.hs-knapsack=${path.custom}/${name.custom.hs-knapsack}/knapsack.py venv.custom.hs-knapsack=${name.solvers}_${name.custom}_${name.custom.hs-knapsack} name.custom.lkh=lkh path.custom.lkh=${path.custom}/${name.custom.lkh}/vrp_lkh.py venv.custom.lkh=${name.solvers}_${name.custom}_${name.custom.lkh} name.custom.berger-vrp=berger-vrp name.custom.sharp-sat-bruteforce=sharp-sat-bruteforce path.custom.sharp-sat-bruteforce=${path.custom}/${name.custom.sharp-sat-bruteforce}/exact-solution-counter.py venv.custom.sharp-sat-bruteforce=${name.solvers}_${name.custom}_${name.custom.sharp-sat-bruteforce} name.custom.sharp-sat-ganak=sharp-sat-ganak venv.custom.sharp-sat-ganak=${name.solvers}_${name.custom}_${name.custom.sharp-sat-ganak} # Demonstrators name.demonstrators=demonstrators name.demonstrators.cplex=cplex path.demonstrators.cplex=${name.demonstrators}/${name.demonstrators.cplex} name.demonstrators.cplex.mip=mip-solver path.demonstrators.cplex.mip=${path.demonstrators.cplex}/${name.demonstrators.cplex.mip}/mip-solver.py venv.demonstrators.cplex.mip=${name.demonstrators}_${name.demonstrators.cplex}_${name.demonstrators.cplex.mip} \ No newline at end of file +# default spring profile, correct one will be set during runtime (see ToolboxServerApplication.java) # options: mac, windows, linux spring.profiles.active=linux springdoc.swagger-ui.operationsSorter=alpha springdoc.swagger-ui.tagsSorter=alpha working.directory=jobs examples.directory=examples springdoc.swagger-ui.path=/ # Solvers name.solvers=solvers # Non OS-specific solvers: (typically GAMS and Python) name.gams=gams path.gams=${name.solvers}/${name.gams} name.gams.max-cut=max-cut path.gams.max-cut=${path.gams}/${name.gams.max-cut}/maxcut.gms name.gams.sat=sat path.gams.sat=${path.gams}/${name.gams.sat}/sat.gms name.qiskit=qiskit path.qiskit=${name.solvers}/${name.qiskit} name.qiskit.knapsack=knapsack path.qiskit.knapsack=${path.qiskit}/${name.qiskit.knapsack}/knapsack_qiskit.py venv.qiskit.knapsack=${name.solvers}_${name.qiskit}_${name.qiskit.knapsack} name.qiskit.max-cut=max-cut path.qiskit.max-cut=${path.qiskit}/${name.qiskit.max-cut}/maxCut_qiskit.py venv.qiskit.max-cut=${name.solvers}_${name.qiskit}_${name.qiskit.max-cut} name.qiskit.qubo=qubo path.qiskit.qubo=${path.qiskit}/${name.qiskit.qubo}/qubo_qiskit.py venv.qiskit.qubo=${name.solvers}_${name.qiskit}_${name.qiskit.qubo} name.cirq=cirq path.cirq=${name.solvers}/${name.cirq} name.cirq.max-cut=max-cut path.cirq.max-cut=${path.cirq}/${name.cirq.max-cut}/max_cut_cirq.py venv.cirq.max-cut=${name.solvers}_${name.cirq}_${name.cirq.max-cut} name.qrisp=qrisp path.qrisp=${name.solvers}/${name.qrisp} name.qrisp.vrp=vrp path.qrisp.vrp=${path.qrisp}/${name.qrisp.vrp}/grover.py venv.qrisp.vrp=${name.solvers}_${name.qrisp}_${name.qrisp.vrp} name.qrisp.qubo=qubo path.qrisp.qubo=${path.qrisp}/${name.qrisp.qubo}/qaoa.py venv.qrisp.qubo=${name.solvers}_${name.qrisp}_${name.qrisp.qubo} name.qrisp.sat=sat path.qrisp.sat.grover=${path.qrisp}/${name.qrisp.sat}/grover.py path.qrisp.sat.exact=${path.qrisp}/${name.qrisp.sat}/exact_grover.py venv.qrisp.sat=${name.solvers}_${name.qrisp}_${name.qrisp.sat} name.dwave=dwave path.dwave=${name.solvers}/${name.dwave} name.dwave.qubo=qubo path.dwave.qubo=${path.dwave}/${name.dwave.qubo}/main.py venv.dwave.qubo=${name.solvers}_${name.dwave}_${name.dwave.qubo} # Non OS-specific custom solvers: (solvers that are not part of a framework) name.custom=custom path.custom=${name.solvers}/${name.custom} name.custom.hs-knapsack=hs-knapsack path.custom.hs-knapsack=${path.custom}/${name.custom.hs-knapsack}/knapsack.py venv.custom.hs-knapsack=${name.solvers}_${name.custom}_${name.custom.hs-knapsack} name.custom.lkh=lkh path.custom.lkh=${path.custom}/${name.custom.lkh}/vrp_lkh.py venv.custom.lkh=${name.solvers}_${name.custom}_${name.custom.lkh} name.custom.berger-vrp=berger-vrp name.custom.sharp-sat-bruteforce=sharp-sat-bruteforce path.custom.sharp-sat-bruteforce=${path.custom}/${name.custom.sharp-sat-bruteforce}/exact-solution-counter.py venv.custom.sharp-sat-bruteforce=${name.solvers}_${name.custom}_${name.custom.sharp-sat-bruteforce} name.custom.sharp-sat-ganak=sharp-sat-ganak venv.custom.sharp-sat-ganak=${name.solvers}_${name.custom}_${name.custom.sharp-sat-ganak} # Demonstrators name.demonstrators=demonstrators name.demonstrators.cplex=cplex path.demonstrators.cplex=${name.demonstrators}/${name.demonstrators.cplex} name.demonstrators.cplex.mip=mip-solver path.demonstrators.cplex.mip=${path.demonstrators.cplex}/${name.demonstrators.cplex.mip}/mip-solver.py venv.demonstrators.cplex.mip=${name.demonstrators}_${name.demonstrators.cplex}_${name.demonstrators.cplex.mip} name.demonstrators.qiskit=qiskit path.demonstrators.qiskit=${name.demonstrators}/${name.demonstrators.qiskit} name.demonstrators.qiskit.molecule-energy=molecule-energy path.demonstrators.qiskit.molecule-energy=${path.demonstrators.qiskit}/${name.demonstrators.qiskit.molecule-energy}/molecule-energy.py venv.demonstrators.qiskit.molecule-energy=${name.demonstrators}_${name.demonstrators.qiskit}_${name.demonstrators.qiskit.molecule-energy} \ No newline at end of file diff --git a/src/test/java/edu/kit/provideq/toolbox/api/CplexMipDemonstratorTest.java b/src/test/java/edu/kit/provideq/toolbox/api/CplexMipDemonstratorTest.java deleted file mode 100644 index 93039c61..00000000 --- a/src/test/java/edu/kit/provideq/toolbox/api/CplexMipDemonstratorTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package edu.kit.provideq.toolbox.api; - -import static edu.kit.provideq.toolbox.demonstrators.DemonstratorConfiguration.DEMONSTRATOR; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import edu.kit.provideq.toolbox.SolutionStatus; -import edu.kit.provideq.toolbox.demonstrators.CplexMipDemonstrator; -import edu.kit.provideq.toolbox.meta.ProblemState; -import java.time.Duration; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.web.reactive.server.WebTestClient; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@SpringBootTest -@AutoConfigureMockMvc -class CplexMipDemonstratorTest { - @Autowired - private WebTestClient client; - - @Autowired - private CplexMipDemonstrator cplexMipDemonstrator; - - @BeforeEach - void beforeEach() { - this.client = this.client.mutate() - .responseTimeout(Duration.ofSeconds(20)) - .build(); - } - - @Test - void testCplexMipDemonstrator() { - var problem = ApiTestHelper.createProblem(client, cplexMipDemonstrator, "", DEMONSTRATOR); - ApiTestHelper.testSolution(problem); - } -} diff --git a/src/test/java/edu/kit/provideq/toolbox/api/DemonstratorTest.java b/src/test/java/edu/kit/provideq/toolbox/api/DemonstratorTest.java new file mode 100644 index 00000000..535ea044 --- /dev/null +++ b/src/test/java/edu/kit/provideq/toolbox/api/DemonstratorTest.java @@ -0,0 +1,52 @@ +package edu.kit.provideq.toolbox.api; + +import static edu.kit.provideq.toolbox.demonstrators.DemonstratorConfiguration.DEMONSTRATOR; + +import edu.kit.provideq.toolbox.meta.ProblemManagerProvider; +import edu.kit.provideq.toolbox.meta.ProblemSolver; +import java.time.Duration; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@SpringBootTest +@AutoConfigureMockMvc +class DemonstratorTest { + @Autowired + private WebTestClient client; + + @Autowired + private ProblemManagerProvider problemManagerProvider; + + @BeforeEach + void beforeEach() { + this.client = this.client.mutate() + .responseTimeout(Duration.ofSeconds(60)) + .build(); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + Stream provideArguments() { + var problemManager = problemManagerProvider.findProblemManagerForType(DEMONSTRATOR).get(); + + return problemManager + .getSolvers() + .stream() + .map(Arguments::of); + } + + @ParameterizedTest + @MethodSource("provideArguments") + void testDemonstratorsWithEmptyInput(ProblemSolver solver) { + var problem = ApiTestHelper.createProblem(client, solver, "", DEMONSTRATOR); + ApiTestHelper.testSolution(problem); + } +} diff --git a/src/test/java/edu/kit/provideq/toolbox/api/FeatureModelAnomalySolversTest.java b/src/test/java/edu/kit/provideq/toolbox/api/FeatureModelAnomalySolversTest.java index 3a493ed1..a9e8e150 100644 --- a/src/test/java/edu/kit/provideq/toolbox/api/FeatureModelAnomalySolversTest.java +++ b/src/test/java/edu/kit/provideq/toolbox/api/FeatureModelAnomalySolversTest.java @@ -37,7 +37,7 @@ class FeatureModelAnomalySolversTest { @BeforeEach void beforeEach() { this.client = this.client.mutate() - .responseTimeout(Duration.ofSeconds(20)) + .responseTimeout(Duration.ofSeconds(60)) .build(); }