Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ jobs:
venv-${{ runner.os }}-

- name: Install solver dependencies
env:
GAMSPY_LICENSE: ${{ secrets.GAMSPY_LICENSE }}
run: ./scripts/ci-setup-solvers.sh

- name: Run checks
shell: bash # required for the source command
run: | # change to "./gradlew check --info" for more debugging output.
source /opt/conda/bin/activate gams
./gradlew check --info
./gradlew check

- name: Archive test reports
if: always()
Expand Down Expand Up @@ -81,4 +83,4 @@ jobs:
command_timeout: 60m
script: |
echo "Deploying ${{ github.event.repository.name }} to '${{ github.ref_name }}' environment"
sh /root/ProvideQ/${{ github.event.repository.name }}/deploy.sh ${{ github.ref_name }}
sh /root/ProvideQ/${{ github.event.repository.name }}/deploy.sh ${{ github.ref_name }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
HELP.md
*.DS_Store
.gradle
.gamspy_license
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
Expand Down
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ jar {
// inject JUnit into test task
tasks.named('test') {
useJUnitPlatform()

// Show test output (System.out.println, etc)
testLogging {
showStandardStreams = true
events "passed", "skipped", "failed"
}
}

tasks.withType(JavaCompile).configureEach {
Expand Down
71 changes: 66 additions & 5 deletions scripts/install-python-dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@
os.path.join(root_dir, 'solvers'),
]

def get_gamspy_license():
# 1. Environment variable (GitHub Actions)
license_key = os.environ.get("GAMSPY_LICENSE")
if license_key:
return license_key.strip()

# 2. Local file (ignored by git)
license_file = os.path.join(root_dir, ".gamspy_license")
if os.path.exists(license_file):
with open(license_file) as f:
return f.read().strip()

raise RuntimeError(
"No GAMSPY license found. "
"Set the GAMSPY_LICENSE environment variable or create a .gamspy_license file."
)

exitCode = 0
for base_dir in base_dirs:
for root, dirs, files in os.walk(base_dir):
Expand All @@ -24,25 +41,69 @@
# Iterate over problem directory (knapsack, tsp, etc.)
for solver_name in os.listdir(framework_dir):
solver_dir = os.path.join(framework_dir, solver_name)

# If folder filter is specified, only process matching folders
if folder_filter and folder_filter not in solver_dir:
continue

req_file = os.path.join(solver_dir, 'requirements.txt')
if os.path.exists(req_file):
venv_name = f"{os.path.basename(root)}_{framework_name}_{solver_name}"
print(f"Setting up virtual environment '{venv_name}' for {solver_dir}...")

print(f"Setting up virtual environment '{venv_name}'")
try:
venv_path = os.path.join('venv', venv_name)
subprocess.run(['python', '-m', 'venv', venv_path], check=True)
subprocess.run(['python', '-m', 'venv', venv_path], check=True, capture_output=True)
if platform.system() == 'Windows':
pip_executable = os.path.join(venv_path, 'Scripts', 'pip.exe')
python_executable = os.path.join(venv_path, 'Scripts', 'python.exe')
else:
pip_executable = os.path.join(venv_path, 'bin', 'pip')
subprocess.run([pip_executable, 'install', '-r', req_file], check=True)
python_executable = os.path.join(venv_path, 'bin', 'python')

# install dependencies from requirements.txt
subprocess.run([pip_executable, 'install', '-r', req_file], check=True, capture_output=True)

# install GAMSPy license if this is a GAMS environment
if "gams" in venv_name.lower():
license_key = get_gamspy_license()

print("Installing GAMSPy license and scip solver ...")
subprocess.run(
[
python_executable,
"-m",
"gamspy",
"install",
"license",
license_key,
],
check=True,
capture_output=True,
)

subprocess.run(
[
python_executable,
"-m",
"gamspy",
"install",
"solver",
"scip"
],
check=True,
capture_output=True,
)

except subprocess.CalledProcessError as e:
print(f"Error setting up virtual environment for {solver_dir}: {e}")
print(f"Error setting up virtual environment '{venv_name}' for {solver_dir}:")
if e.stdout:
print("STDOUT:")
print(e.stdout.decode() if isinstance(e.stdout, bytes) else e.stdout)
if e.stderr:
print("STDERR:")
print(e.stderr.decode() if isinstance(e.stderr, bytes) else e.stderr)
exitCode = 1

# let pipeline fail if there was an error in the venv setup.
exit(exitCode)
exit(exitCode)
5 changes: 3 additions & 2 deletions solvers/gams/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# This file describes the python package requirements for all GAMS solvers.
# We only build one venv for all of GAMS, not for every GAMS solver.
# This file contains of the python dependencies required in the .gms files
# GamsPy solvers are handles extra.
networkx
gamspy == 1.23.1
5 changes: 3 additions & 2 deletions solvers/gams/qubo/solver_lp_qubo.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ def parse_lp_to_df(filepath="unsplittable_model.lp"):
)

print("Solving model natively...")
qubo_model.solve(solver="CPLEX", output=sys.stdout, options=gp.Options(time_limit=60))
qubo_model.solve(solver="SCIP", solver_options={"lp/solver": '"highs"'},
output=sys.stdout, options=gp.Options(time_limit=60))

print(f"\n--- Solver Status: {qubo_model.status} ---")
print(f"--- Objective Value: {qubo_model.objective_value} ---")
Expand All @@ -88,4 +89,4 @@ def parse_lp_to_df(filepath="unsplittable_model.lp"):
with open(output_path, "w") as f:
json.dump(solution_dict, f, indent=4)

print(f"--- Extracted {len(solution_dict)} active variables to {output_path} ---")
print(f"--- Extracted {len(solution_dict)} active variables to {output_path} ---")
2 changes: 1 addition & 1 deletion solvers/gams/unsplittable-mcf/solver_classical.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@
model.build_equations_and_model()

print("\n--- Solving Classical MIP with CPLEX ---")
model.solve_classical(solver="CPLEX", output_html=output_path)
model.solve_classical(solver="SCIP", output_html=output_path)
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ public GamsQuboSolver(

@Override
public String getName() {
return "(GAMS) CPLEX Solver for QUBOs";
return "(GAMS) SCIP Solver for QUBOs";
}

@Override
public String getDescription() {
return "Solves QUBO problems by transforming it into a MIP,"
+ "which is then solved with CPLEX. Implementation is done in GAMS";
+ "which is then solved with SCIP. Implementation is done in GAMS";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ public GamsUnsplittableMcfClassicalSolver(

@Override
public String getName() {
return "(GAMS) CPLEX Classical MCF Solver";
return "(GAMS) SCIP Classical MCF Solver";
}

@Override
public String getDescription() {
return "Solves the Unsplittable Multi Commodity Flow problem using GAMSPy with CPLEX. "
return "Solves the Unsplittable Multi Commodity Flow problem using GAMSPy and SCIP. "
+ "Builds a time-expanded network model and finds optimal flow routes minimizing "
+ "delay and slack penalties.";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ public static <InputT, ResultT> ProblemDto<InputT, ResultT> trySolveFor(
// print error output if something went wrong
if (problemDto.getState() != ProblemState.SOLVED
|| problemDto.getSolution().getStatus() != SolutionStatus.SOLVED) {
System.out.println("Testcase failed. Printing debugging info:");
System.out.println(builder);
}

Expand Down
Loading