From 3a3a049889dc323bb162f27f6be2d2146320c61a Mon Sep 17 00:00:00 2001 From: FiveMovesAhead Date: Mon, 1 Dec 2025 15:19:07 +0000 Subject: [PATCH] Submitted satisfiability/schnoing --- tig-algorithms/src/satisfiability/mod.rs | 3 +- .../src/satisfiability/schnoing/README.md | 23 +++ .../satisfiability/schnoing/cuda_example.rs | 156 ++++++++++++++++++ .../src/satisfiability/schnoing/mod.rs | 61 +++++++ 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 tig-algorithms/src/satisfiability/schnoing/README.md create mode 100644 tig-algorithms/src/satisfiability/schnoing/cuda_example.rs create mode 100644 tig-algorithms/src/satisfiability/schnoing/mod.rs diff --git a/tig-algorithms/src/satisfiability/mod.rs b/tig-algorithms/src/satisfiability/mod.rs index 22d6805c..e6bcd840 100644 --- a/tig-algorithms/src/satisfiability/mod.rs +++ b/tig-algorithms/src/satisfiability/mod.rs @@ -1,4 +1,5 @@ -// c001_a001 +pub mod schnoing; +pub use schnoing as c001_a001; // c001_a002 diff --git a/tig-algorithms/src/satisfiability/schnoing/README.md b/tig-algorithms/src/satisfiability/schnoing/README.md new file mode 100644 index 00000000..3a993365 --- /dev/null +++ b/tig-algorithms/src/satisfiability/schnoing/README.md @@ -0,0 +1,23 @@ +# TIG Code Submission + +## Submission Details + +* **Challenge Name:** satisfiability +* **Algorithm Name:** schnoing +* **Copyright:** 2024 Uncharted Trading Limited +* **Identity of Submitter:** Uncharted Trading Limited +* **Identity of Creator of Algorithmic Method:** null +* **Unique Algorithm Identifier (UAI):** null + +## License + +The files in this folder are under the following licenses: +* TIG Benchmarker Outbound License +* TIG Commercial License +* TIG Inbound Game License +* TIG Innovator Outbound Game License +* TIG Open Data License +* TIG THV Game License + +Copies of the licenses can be obtained at: +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/schnoing/cuda_example.rs b/tig-algorithms/src/satisfiability/schnoing/cuda_example.rs new file mode 100644 index 00000000..908b9ed2 --- /dev/null +++ b/tig-algorithms/src/satisfiability/schnoing/cuda_example.rs @@ -0,0 +1,156 @@ +/*! +Copyright 2024 TIG Foundation + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(challenge.seeds[0] as u64); + let num_variables = challenge.difficulty.num_variables; + let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); + + // Pre-generate a bunch of random integers + // IMPORTANT! When generating random numbers, never use usize! usize bytes varies from system to system + let rand_ints: Vec = (0..2 * num_variables) + .map(|_| rng.gen_range(0..1_000_000_000u32) as usize) + .collect(); + + for i in 0..num_variables { + // Evaluate clauses and find any that are unsatisfied + let substituted: Vec = challenge + .clauses + .iter() + .map(|clause| { + clause.iter().any(|&literal| { + let var_idx = literal.abs() as usize - 1; + let var_value = variables[var_idx]; + (literal > 0 && var_value) || (literal < 0 && !var_value) + }) + }) + .collect(); + + let unsatisfied_clauses: Vec = substituted + .iter() + .enumerate() + .filter_map(|(idx, &satisfied)| if !satisfied { Some(idx) } else { None }) + .collect(); + + let num_unsatisfied_clauses = unsatisfied_clauses.len(); + if num_unsatisfied_clauses == 0 { + break; + } + + // Flip the value of a random variable from a random unsatisfied clause + let rand_unsatisfied_clause_idx = rand_ints[2 * i] % num_unsatisfied_clauses; + let rand_unsatisfied_clause = unsatisfied_clauses[rand_unsatisfied_clause_idx]; + let rand_variable_idx = rand_ints[2 * i + 1] % 3; + let rand_variable = + challenge.clauses[rand_unsatisfied_clause][rand_variable_idx].abs() as usize - 1; + variables[rand_variable] = !variables[rand_variable]; + } + + Ok(Some(Solution { variables })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = Some(CudaKernel { + // Example CUDA code from https://github.com/coreylowman/cudarc/blob/main/examples/matmul-kernel.rs + src: r#" +extern "C" __global__ void matmul(float* A, float* B, float* C, int N) { + int ROW = blockIdx.y*blockDim.y+threadIdx.y; + int COL = blockIdx.x*blockDim.x+threadIdx.x; + + float tmpSum = 0; + + if (ROW < N && COL < N) { + // each thread computes one element of the block sub-matrix + for (int i = 0; i < N; i++) { + tmpSum += A[ROW * N + i] * B[i * N + COL]; + } + } + C[ROW * N + COL] = tmpSum; +} +"#, + funcs: &["matmul"], + }); + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + // Example CUDA code from https://github.com/coreylowman/cudarc/blob/main/examples/matmul-kernel.rs + let start = std::time::Instant::now(); + + let a_host = [1.0f32, 2.0, 3.0, 4.0]; + let b_host = [1.0f32, 2.0, 3.0, 4.0]; + let mut c_host = [0.0f32; 4]; + + let a_dev = dev.htod_sync_copy(&a_host)?; + let b_dev = dev.htod_sync_copy(&b_host)?; + let mut c_dev = dev.htod_sync_copy(&c_host)?; + + println!("Copied in {:?}", start.elapsed()); + + let cfg = LaunchConfig { + block_dim: (2, 2, 1), + grid_dim: (1, 1, 1), + shared_mem_bytes: 0, + }; + unsafe { + funcs + .remove("matmul") + .unwrap() + .launch(cfg, (&a_dev, &b_dev, &mut c_dev, 2i32)) + }?; + + dev.dtoh_sync_copy_into(&c_dev, &mut c_host)?; + println!("Found {:?} in {:?}", c_host, start.elapsed()); + + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/schnoing/mod.rs b/tig-algorithms/src/satisfiability/schnoing/mod.rs new file mode 100644 index 00000000..60c1bfa1 --- /dev/null +++ b/tig-algorithms/src/satisfiability/schnoing/mod.rs @@ -0,0 +1,61 @@ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use serde_json::{Map, Value}; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge( + challenge: &Challenge, + save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>, + hyperparameters: &Option>, +) -> anyhow::Result<()> { + let _ = save_solution(&Solution { variables: vec![false; challenge.num_variables] }); + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + let num_variables = challenge.num_variables; + let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); + + // Pre-generate a bunch of random integers + // IMPORTANT! When generating random numbers, never use usize! usize bytes varies from system to system + let rand_ints: Vec = (0..2 * num_variables) + .map(|_| rng.gen_range(0..1_000_000_000u32) as usize) + .collect(); + + for i in 0..num_variables { + // Evaluate clauses and find any that are unsatisfied + let substituted: Vec = challenge + .clauses + .iter() + .map(|clause| { + clause.iter().any(|&literal| { + let var_idx = literal.abs() as usize - 1; + let var_value = variables[var_idx]; + (literal > 0 && var_value) || (literal < 0 && !var_value) + }) + }) + .collect(); + + let unsatisfied_clauses: Vec = substituted + .iter() + .enumerate() + .filter_map(|(idx, &satisfied)| if !satisfied { Some(idx) } else { None }) + .collect(); + + let num_unsatisfied_clauses = unsatisfied_clauses.len(); + if num_unsatisfied_clauses == 0 { + break; + } + + // Flip the value of a random variable from a random unsatisfied clause + let rand_unsatisfied_clause_idx = rand_ints[2 * i] % num_unsatisfied_clauses; + let rand_unsatisfied_clause = unsatisfied_clauses[rand_unsatisfied_clause_idx]; + let rand_variable_idx = rand_ints[2 * i + 1] % 3; + let rand_variable = + challenge.clauses[rand_unsatisfied_clause][rand_variable_idx].abs() as usize - 1; + variables[rand_variable] = !variables[rand_variable]; + } + + let _ = save_solution(&Solution { variables }); + return Ok(()); +} + +pub fn help() { + println!("No help information available."); +}