diff --git a/cuda_keyring.deb b/cuda_keyring.deb deleted file mode 100644 index 559906a..0000000 Binary files a/cuda_keyring.deb and /dev/null differ diff --git a/funding.json b/funding.json deleted file mode 100644 index 3956747..0000000 --- a/funding.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "opRetro": { - "projectId": "0xb0898866c2c537c61b37d04916e3879ef78fed630181c1ffaf492499d174f0bf" - } -} diff --git a/scripts/list_algorithms.sh b/scripts/list_algorithms.sh index bdbe2c8..a4c9d74 100644 --- a/scripts/list_algorithms.sh +++ b/scripts/list_algorithms.sh @@ -5,7 +5,7 @@ BLOCK_ID=$(curl -s https://mainnet-api.tig.foundation/get-block | jq -r '.block. RESP=$(curl -s "https://mainnet-api.tig.foundation/get-algorithms?block_id=$BLOCK_ID") ALGORITHMS=$(echo $RESP | jq -c '.algorithms[]' | jq -s 'sort_by(.id)') -WASMS_DICT=$(echo $RESP | jq -c '[.wasms[] | {key: .algorithm_id, value: .}] | from_entries') +WASMS_DICT=$(echo $RESP | jq -c '[.binarys[] | {key: .algorithm_id, value: .}] | from_entries') for ALGO in $(echo $ALGORITHMS | jq -c '.[]'); do ID=$(echo $ALGO | jq -r '.id') diff --git a/scripts/list_block_benchmark_ids.sh b/scripts/list_block_benchmark_ids.sh index 68a718a..697dbf0 100644 --- a/scripts/list_block_benchmark_ids.sh +++ b/scripts/list_block_benchmark_ids.sh @@ -1,3 +1,3 @@ #!/bin/bash set -e -curl -s https://mainnet-api.tig.foundation/get-block?include_data | jq -r '.block.data.active_benchmark_ids[]' | nl \ No newline at end of file +curl -s https://mainnet-api.tig.foundation/get-block?include_data | jq -r '.block.data.active_ids.benchmark[]' | nl \ No newline at end of file diff --git a/scripts/list_players.sh b/scripts/list_players.sh index 674ee4c..6f4f56e 100644 --- a/scripts/list_players.sh +++ b/scripts/list_players.sh @@ -2,13 +2,12 @@ set -e BLOCK_ID=$(curl -s https://mainnet-api.tig.foundation/get-block | jq -r '.block.id') -RESP=$(curl -s "https://mainnet-api.tig.foundation/get-players?player_type=benchmarker&block_id=$BLOCK_ID") +RESP=$(curl -s "https://mainnet-api.tig.foundation/get-opow?block_id=$BLOCK_ID") -PLAYERS=$(echo $RESP | jq -c '[.players[] | .block_data.reward = (if .block_data.reward == null then 0 else (.block_data.reward | tonumber) end)] | sort_by(.block_data.reward) | reverse') +PLAYERS=$(echo $RESP | jq -c '[.opow[] | .block_data.reward = (if .block_data.reward == null then 0 else (.block_data.reward | tonumber) end)] | sort_by(.block_data.reward) | reverse') for PLAYER in $(echo $PLAYERS | jq -c '.[]'); do - ID=$(echo $PLAYER | jq -r '.id') - ROUND_EARNINGS=$(echo $PLAYER | jq -r '.block_data.round_earnings | tonumber / 1e18') + ID=$(echo $PLAYER | jq -r '.player_id') REWARD=$(echo $PLAYER | jq -r '.block_data.reward | if . == null then "null" else tonumber / 1e18 end') - printf "Player ID: %-25s Round Earnings: %-20s Reward: %-20s\n" "$ID" "$ROUND_EARNINGS" "$REWARD" + printf "Player ID: %-25s Reward: %-20s\n" "$ID" "$REWARD" done \ No newline at end of file diff --git a/scripts/test_algorithm.sh b/scripts/test_algorithm.sh index fb6ebf2..edc2fdf 100644 --- a/scripts/test_algorithm.sh +++ b/scripts/test_algorithm.sh @@ -77,7 +77,7 @@ if ! is_positive_integer "$num_workers"; then exit 1 fi read -p "Enter max fuel (default is 10000000000): " max_fuel -max_fuel=${max_fuel:-1} +max_fuel=${max_fuel:-10000000000} if ! is_positive_integer "$max_fuel"; then echo "Error: Max fuel must be a positive integer." exit 1 diff --git a/swagger.yaml b/swagger.yaml index 1d63d4d..9183b82 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -153,6 +153,31 @@ paths: application/json: schema: $ref: '#/components/schemas/GetBlockResponse' + /get-delegators: + get: + tags: + - GET + summary: Get latest delegators data + description: |- + # Notes + + * Query parameter `` must be the latest block. Use `/get-block` endpoint + + * All players who have a deposit are included in the response + + parameters: + - name: block_id + in: query + required: true + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/GetDelegatorsResponse' /get-difficulty-data: get: tags: @@ -271,8 +296,19 @@ paths: description: |- # Notes * `algorithms` is a map of `` to `uint256` - * `opow` is a map of `` to `uint256` - * `players` is a map of `` to dict of reward types (algorithm, benchmark, breakthroughs, delegator) + + * `breakthroughs` is a map of `` to `uint256` + + * `opow` is a map of `` to a dict: + + * `total` is the total reward earned by the Benchmarker this round before any sharing is deducted + + * `shared` is the total reward shared by the Benchmarker with delegators this round + + * `coinbase` is the total reward distributed by the Benchmarker with pool members this round + + * `players` is a map of `` to a dict of reward types (algorithm, benchmark, breakthroughs, delegator) + * `names` is a map of `` to ENS name (only if one exists) parameters: - name: round @@ -313,6 +349,38 @@ paths: application/json: schema: $ref: '#/components/schemas/RequestApiKeyResponse' + /set-coinbase: + post: + tags: + - POST + summary: Set distribution of your reward amongst pool members + description: |- + # Notes + * Can only be updated once every `block.config.opow.coinbase_update_period` blocks + + * Header `X-Api-Key` is required. Use `/request-api-key` endpoint. + + * If `` is invalid, a `401` error will be returned + + * `` is a map of `` to `float`, where the floats must sum to at most `1.0` + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SetCoinbaseRequest' + parameters: + - in: header + name: X-Api-Key + description: from /request-api-key endpoint + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SetCoinbaseResponse' /set-delegatees: post: tags: @@ -376,6 +444,42 @@ paths: application/json: schema: $ref: '#/components/schemas/SetRewardShareResponse' + /set-vote: + post: + tags: + - POST + summary: Cast vote on breakthrough + description: |- + # Notes + * Votes are immutable. Once set, cannot change + + * Header `X-Api-Key` is required. Use `/request-api-key` endpoint. + + * If `` is invalid, a `401` error will be returned + + * Signature must be from signing the following message: + + * If voting yes: `I hereby confirm my vote that '' IS A BREAKTHROUGH` + + * If voting no: `I hereby confirm my vote that '' IS INELIGIBLE AS A BREAKTHROUGH` + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SetVoteRequest' + parameters: + - in: header + name: X-Api-Key + description: from /request-api-key endpoint + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SetVoteResponse' /submit-algorithm: post: tags: @@ -386,9 +490,7 @@ paths: * This endpoint can only be invoked once every few seconds - * If an algorithm submission has failed to compile (`wasm.state.compiled_success = false`), you can re-use the same algorithm name. - - * `` is the id of the transaction that has burnt the required `block.config.algorithm_submissions.submission_fee` TIG to the `block.config.erc20.burn_address` + * If an algorithm submission has failed to compile (`binary.state.compiled_success = false`), you can re-use the same algorithm name. * Header `X-Api-Key` is required. Use `/request-api-key` endpoint. @@ -446,38 +548,6 @@ paths: application/json: schema: $ref: '#/components/schemas/SubmitBenchmarkResponse' - /submit-deposit: - post: - tags: - - POST - summary: Submit a deposit - description: |- - # Notes - - * `` is the id of the transaction that has sent at least `block.config.deposits.min_lock_amount` TIG to `block.config.deposits.lock_address`. Additionally: - * Must be non-cancellable - * Must be non-transferrable - * Minimum lock duration must be at least 1 week (604800 seconds) - * `` is the log index of the CreateLockupLinearStream event - * If `` is null, then the first CreateLockupLinearStream event will be used - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SubmitDepositRequest' - parameters: - - in: header - name: X-Api-Key - description: from /request-api-key endpoint - schema: - $ref: '#/components/schemas/MD5' - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/SubmitDepositResponse' /submit-precommit: post: tags: @@ -536,36 +606,6 @@ paths: application/json: schema: $ref: '#/components/schemas/SubmitProofResponse' - /submit-topup: - post: - tags: - - POST - summary: Submit a topup transaction - description: |- - # Notes - - * `` is the id of the transaction that has sent at least `block.config.topups.topup_amount` TIG to the `block.config.topups.topup_address` - * `` is the log index of the CreateLockupLinearStream event - * If `` is null, then the first CreateLockupLinearStream event will be used - - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SubmitTopupRequest' - parameters: - - in: header - name: X-Api-Key - description: from /request-api-key endpoint - schema: - $ref: '#/components/schemas/MD5' - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/SubmitTopupResponse' components: schemas: Address: @@ -919,6 +959,12 @@ components: round_pushed: type: integer format: uint32 + round_voting_starts: + type: integer + format: uint32 + round_votes_tallied: + type: integer + format: uint32 round_active: type: integer format: uint32 @@ -949,6 +995,9 @@ components: num_qualifiers: type: integer format: uint32 + average_solution_ratio: + type: number + format: double qualifier_difficulties: type: array items: @@ -1021,6 +1070,8 @@ components: DifficultyData: type: object properties: + algorithm_id: + $ref: '#/components/schemas/AlgorithmId' num_nonces: type: integer format: uint64 @@ -1083,18 +1134,29 @@ components: type: integer format: uint32 example: {"c001": 5, "c002": 3} + solution_ratio_by_challenge: + type: object + additionalProperties: + type: double + format: number + example: {"c001": 0.001, "c002": 0.5} cutoff: type: integer format: uint32 - associated_deposit: + self_deposit: + $ref: '#/components/schemas/PreciseNumber' + delegated_weighted_deposit: $ref: '#/components/schemas/PreciseNumber' delegators: type: array items: $ref: '#/components/schemas/Address' + coinbase: + type: object + additionalProperties: + $ref: '#/components/schemas/PreciseNumber' reward_share: - type: number - format: double + $ref: '#/components/schemas/PreciseNumber' imbalance: $ref: '#/components/schemas/PreciseNumber' influence: @@ -1348,6 +1410,25 @@ components: type: array items: $ref: '#/components/schemas/Challenge' + GetDelegatorsResponse: + type: object + properties: + delegators: + type: array + items: + type: object + properties: + player_id: + $ref: '#/components/schemas/Address' + deposit: + $ref: '#/components/schemas/PreciseNumber' + delegatees: + type: object + additionalProperties: + type: number + format: double + reward: + $ref: '#/components/schemas/PreciseNumber' GetDifficultyDataResponse: type: object properties: @@ -1386,10 +1467,23 @@ components: type: object additionalProperties: $ref: '#/components/schemas/PreciseNumber' - opow: + breakthroughs: type: object additionalProperties: $ref: '#/components/schemas/PreciseNumber' + opow: + type: object + additionalProperties: + type: object + properties: + total: + $ref: '#/components/schemas/PreciseNumber' + shared: + $ref: '#/components/schemas/PreciseNumber' + coinbase: + type: object + additionalProperties: + $ref: '#/components/schemas/PreciseNumber' players: type: object additionalProperties: @@ -1422,13 +1516,25 @@ components: $ref: '#/components/schemas/MD5' name: type: string + SetCoinbaseRequest: + type: object + properties: + coinbase: + type: number + format: double + SetCoinbaseResponse: + type: object + properties: + ok: + type: boolean SetDelegateesRequest: type: object properties: delegatees: type: object additionalProperties: - type: float + type: number + format: double SetDelegateeResponse: type: object properties: @@ -1445,6 +1551,20 @@ components: properties: ok: type: boolean + SetVoteRequest: + type: object + properties: + breakthrough_id: + type: string + yes: + type: boolean + signature: + $ref: '#/components/schemas/Signature' + SetVoteResponse: + type: object + properties: + ok: + type: boolean SubmitAlgorithmRequest: type: object properties: @@ -1476,19 +1596,6 @@ components: properties: ok: type: boolean - SubmitDepositRequest: - type: object - properties: - tx_hash: - $ref: '#/components/schemas/TxHash' - log_idx: - type: integer - format: uint32 - SubmitDepositResponse: - type: object - properties: - deposit_id: - $ref: '#/components/schemas/MD5' SubmitPrecommitRequest: type: object properties: @@ -1515,17 +1622,4 @@ components: type: object properties: verified: - type: string - SubmitTopupRequest: - type: object - properties: - tx_hash: - $ref: '#/components/schemas/TxHash' - log_idx: - type: integer - format: uint32 - SubmitTopupResponse: - type: object - properties: - topup_id: - $ref: '#/components/schemas/MD5' \ No newline at end of file + type: string \ No newline at end of file diff --git a/tig-algorithms/Cargo.toml b/tig-algorithms/Cargo.toml index ab1cc50..bbca2f9 100644 --- a/tig-algorithms/Cargo.toml +++ b/tig-algorithms/Cargo.toml @@ -12,7 +12,10 @@ cudarc = { version = "0.12.0", features = [ "cuda-version-from-build-system", ], optional = true } ndarray = "0.15.6" -rand = { version = "0.8.5", default-features = false, features = ["std_rng"] } +rand = { version = "0.8.5", default-features = false, features = [ + "std_rng", + "small_rng", +] } tig-challenges = { path = "../tig-challenges" } [lib] diff --git a/tig-algorithms/src/knapsack/classic_quadkp/benchmarker_outbound.rs b/tig-algorithms/src/knapsack/classic_quadkp/benchmarker_outbound.rs deleted file mode 100644 index 4f65bef..0000000 --- a/tig-algorithms/src/knapsack/classic_quadkp/benchmarker_outbound.rs +++ /dev/null @@ -1,167 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 anyhow::Result; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut edge_costs: Vec<(usize, f32)> = (0..vertex_count) - .map(|flow_index| { - let total_flow = challenge.values[flow_index] as i32 + - challenge.interaction_values[flow_index].iter().sum::(); - let cost = total_flow as f32 / challenge.weights[flow_index] as f32; - (flow_index, cost) - }) - .collect(); - - edge_costs.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut coloring = Vec::with_capacity(vertex_count); - let mut uncolored = Vec::with_capacity(vertex_count); - let mut current_entropy = 0; - let mut current_temperature = 0; - - for &(flow_index, _) in &edge_costs { - if current_entropy + challenge.weights[flow_index] <= challenge.max_weight { - current_entropy += challenge.weights[flow_index]; - current_temperature += challenge.values[flow_index] as i32; - - for &colored in &coloring { - current_temperature += challenge.interaction_values[flow_index][colored]; - } - coloring.push(flow_index); - } else { - uncolored.push(flow_index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for flow_index in 0..vertex_count { - mutation_rates[flow_index] = challenge.values[flow_index] as i32; - for &colored in &coloring { - mutation_rates[flow_index] += challenge.interaction_values[flow_index][colored]; - } - } - - let max_generations = 100; - let mut cooling_schedule = vec![0; vertex_count]; - - for _ in 0..max_generations { - let mut best_mutation = 0; - let mut best_crossover = None; - - for uncolored_index in 0..uncolored.len() { - let mutant = uncolored[uncolored_index]; - if cooling_schedule[mutant] > 0 { - continue; - } - - unsafe { - let mutant_fitness = *mutation_rates.get_unchecked(mutant); - let min_entropy_reduction = *challenge.weights.get_unchecked(mutant) as i32 - (challenge.max_weight as i32 - current_entropy as i32); - - if mutant_fitness < 0 { - continue; - } - - for colored_index in 0..coloring.len() { - let gene_to_remove = *coloring.get_unchecked(colored_index); - if *cooling_schedule.get_unchecked(gene_to_remove) > 0 { - continue; - } - - if min_entropy_reduction > 0 { - let removed_entropy = *challenge.weights.get_unchecked(gene_to_remove) as i32; - if removed_entropy < min_entropy_reduction { - continue; - } - } - - let fitness_change = mutant_fitness - *mutation_rates.get_unchecked(gene_to_remove) - - *challenge.interaction_values.get_unchecked(mutant).get_unchecked(gene_to_remove); - - if fitness_change > best_mutation { - best_mutation = fitness_change; - best_crossover = Some((uncolored_index, colored_index)); - } - } - } - } - - if let Some((uncolored_index, colored_index)) = best_crossover { - let gene_to_add = uncolored[uncolored_index]; - let gene_to_remove = coloring[colored_index]; - - coloring.swap_remove(colored_index); - uncolored.swap_remove(uncolored_index); - coloring.push(gene_to_add); - uncolored.push(gene_to_remove); - - current_temperature += best_mutation; - current_entropy = current_entropy + challenge.weights[gene_to_add] - challenge.weights[gene_to_remove]; - - unsafe { - for flow_index in 0..vertex_count { - *mutation_rates.get_unchecked_mut(flow_index) += - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_add) - - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_remove); - } - } - - cooling_schedule[gene_to_add] = 3; - cooling_schedule[gene_to_remove] = 3; - } else { - break; - } - - if current_temperature as u32 >= challenge.min_value { - return Ok(Some(Solution { items: coloring })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - } - - if current_temperature as u32 >= challenge.min_value { - Ok(Some(Solution { items: coloring })) - } else { - Ok(None) - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/classic_quadkp/commercial.rs b/tig-algorithms/src/knapsack/classic_quadkp/commercial.rs deleted file mode 100644 index c8ff1e3..0000000 --- a/tig-algorithms/src/knapsack/classic_quadkp/commercial.rs +++ /dev/null @@ -1,167 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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 anyhow::Result; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut edge_costs: Vec<(usize, f32)> = (0..vertex_count) - .map(|flow_index| { - let total_flow = challenge.values[flow_index] as i32 + - challenge.interaction_values[flow_index].iter().sum::(); - let cost = total_flow as f32 / challenge.weights[flow_index] as f32; - (flow_index, cost) - }) - .collect(); - - edge_costs.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut coloring = Vec::with_capacity(vertex_count); - let mut uncolored = Vec::with_capacity(vertex_count); - let mut current_entropy = 0; - let mut current_temperature = 0; - - for &(flow_index, _) in &edge_costs { - if current_entropy + challenge.weights[flow_index] <= challenge.max_weight { - current_entropy += challenge.weights[flow_index]; - current_temperature += challenge.values[flow_index] as i32; - - for &colored in &coloring { - current_temperature += challenge.interaction_values[flow_index][colored]; - } - coloring.push(flow_index); - } else { - uncolored.push(flow_index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for flow_index in 0..vertex_count { - mutation_rates[flow_index] = challenge.values[flow_index] as i32; - for &colored in &coloring { - mutation_rates[flow_index] += challenge.interaction_values[flow_index][colored]; - } - } - - let max_generations = 100; - let mut cooling_schedule = vec![0; vertex_count]; - - for _ in 0..max_generations { - let mut best_mutation = 0; - let mut best_crossover = None; - - for uncolored_index in 0..uncolored.len() { - let mutant = uncolored[uncolored_index]; - if cooling_schedule[mutant] > 0 { - continue; - } - - unsafe { - let mutant_fitness = *mutation_rates.get_unchecked(mutant); - let min_entropy_reduction = *challenge.weights.get_unchecked(mutant) as i32 - (challenge.max_weight as i32 - current_entropy as i32); - - if mutant_fitness < 0 { - continue; - } - - for colored_index in 0..coloring.len() { - let gene_to_remove = *coloring.get_unchecked(colored_index); - if *cooling_schedule.get_unchecked(gene_to_remove) > 0 { - continue; - } - - if min_entropy_reduction > 0 { - let removed_entropy = *challenge.weights.get_unchecked(gene_to_remove) as i32; - if removed_entropy < min_entropy_reduction { - continue; - } - } - - let fitness_change = mutant_fitness - *mutation_rates.get_unchecked(gene_to_remove) - - *challenge.interaction_values.get_unchecked(mutant).get_unchecked(gene_to_remove); - - if fitness_change > best_mutation { - best_mutation = fitness_change; - best_crossover = Some((uncolored_index, colored_index)); - } - } - } - } - - if let Some((uncolored_index, colored_index)) = best_crossover { - let gene_to_add = uncolored[uncolored_index]; - let gene_to_remove = coloring[colored_index]; - - coloring.swap_remove(colored_index); - uncolored.swap_remove(uncolored_index); - coloring.push(gene_to_add); - uncolored.push(gene_to_remove); - - current_temperature += best_mutation; - current_entropy = current_entropy + challenge.weights[gene_to_add] - challenge.weights[gene_to_remove]; - - unsafe { - for flow_index in 0..vertex_count { - *mutation_rates.get_unchecked_mut(flow_index) += - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_add) - - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_remove); - } - } - - cooling_schedule[gene_to_add] = 3; - cooling_schedule[gene_to_remove] = 3; - } else { - break; - } - - if current_temperature as u32 >= challenge.min_value { - return Ok(Some(Solution { items: coloring })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - } - - if current_temperature as u32 >= challenge.min_value { - Ok(Some(Solution { items: coloring })) - } else { - Ok(None) - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/classic_quadkp/inbound.rs b/tig-algorithms/src/knapsack/classic_quadkp/inbound.rs deleted file mode 100644 index 314acfb..0000000 --- a/tig-algorithms/src/knapsack/classic_quadkp/inbound.rs +++ /dev/null @@ -1,167 +0,0 @@ -/*! -Copyright 2024 syebastian - -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 anyhow::Result; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut edge_costs: Vec<(usize, f32)> = (0..vertex_count) - .map(|flow_index| { - let total_flow = challenge.values[flow_index] as i32 + - challenge.interaction_values[flow_index].iter().sum::(); - let cost = total_flow as f32 / challenge.weights[flow_index] as f32; - (flow_index, cost) - }) - .collect(); - - edge_costs.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut coloring = Vec::with_capacity(vertex_count); - let mut uncolored = Vec::with_capacity(vertex_count); - let mut current_entropy = 0; - let mut current_temperature = 0; - - for &(flow_index, _) in &edge_costs { - if current_entropy + challenge.weights[flow_index] <= challenge.max_weight { - current_entropy += challenge.weights[flow_index]; - current_temperature += challenge.values[flow_index] as i32; - - for &colored in &coloring { - current_temperature += challenge.interaction_values[flow_index][colored]; - } - coloring.push(flow_index); - } else { - uncolored.push(flow_index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for flow_index in 0..vertex_count { - mutation_rates[flow_index] = challenge.values[flow_index] as i32; - for &colored in &coloring { - mutation_rates[flow_index] += challenge.interaction_values[flow_index][colored]; - } - } - - let max_generations = 100; - let mut cooling_schedule = vec![0; vertex_count]; - - for _ in 0..max_generations { - let mut best_mutation = 0; - let mut best_crossover = None; - - for uncolored_index in 0..uncolored.len() { - let mutant = uncolored[uncolored_index]; - if cooling_schedule[mutant] > 0 { - continue; - } - - unsafe { - let mutant_fitness = *mutation_rates.get_unchecked(mutant); - let min_entropy_reduction = *challenge.weights.get_unchecked(mutant) as i32 - (challenge.max_weight as i32 - current_entropy as i32); - - if mutant_fitness < 0 { - continue; - } - - for colored_index in 0..coloring.len() { - let gene_to_remove = *coloring.get_unchecked(colored_index); - if *cooling_schedule.get_unchecked(gene_to_remove) > 0 { - continue; - } - - if min_entropy_reduction > 0 { - let removed_entropy = *challenge.weights.get_unchecked(gene_to_remove) as i32; - if removed_entropy < min_entropy_reduction { - continue; - } - } - - let fitness_change = mutant_fitness - *mutation_rates.get_unchecked(gene_to_remove) - - *challenge.interaction_values.get_unchecked(mutant).get_unchecked(gene_to_remove); - - if fitness_change > best_mutation { - best_mutation = fitness_change; - best_crossover = Some((uncolored_index, colored_index)); - } - } - } - } - - if let Some((uncolored_index, colored_index)) = best_crossover { - let gene_to_add = uncolored[uncolored_index]; - let gene_to_remove = coloring[colored_index]; - - coloring.swap_remove(colored_index); - uncolored.swap_remove(uncolored_index); - coloring.push(gene_to_add); - uncolored.push(gene_to_remove); - - current_temperature += best_mutation; - current_entropy = current_entropy + challenge.weights[gene_to_add] - challenge.weights[gene_to_remove]; - - unsafe { - for flow_index in 0..vertex_count { - *mutation_rates.get_unchecked_mut(flow_index) += - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_add) - - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_remove); - } - } - - cooling_schedule[gene_to_add] = 3; - cooling_schedule[gene_to_remove] = 3; - } else { - break; - } - - if current_temperature as u32 >= challenge.min_value { - return Ok(Some(Solution { items: coloring })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - } - - if current_temperature as u32 >= challenge.min_value { - Ok(Some(Solution { items: coloring })) - } else { - Ok(None) - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/classic_quadkp/innovator_outbound.rs b/tig-algorithms/src/knapsack/classic_quadkp/innovator_outbound.rs deleted file mode 100644 index 3c9de50..0000000 --- a/tig-algorithms/src/knapsack/classic_quadkp/innovator_outbound.rs +++ /dev/null @@ -1,167 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 anyhow::Result; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut edge_costs: Vec<(usize, f32)> = (0..vertex_count) - .map(|flow_index| { - let total_flow = challenge.values[flow_index] as i32 + - challenge.interaction_values[flow_index].iter().sum::(); - let cost = total_flow as f32 / challenge.weights[flow_index] as f32; - (flow_index, cost) - }) - .collect(); - - edge_costs.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut coloring = Vec::with_capacity(vertex_count); - let mut uncolored = Vec::with_capacity(vertex_count); - let mut current_entropy = 0; - let mut current_temperature = 0; - - for &(flow_index, _) in &edge_costs { - if current_entropy + challenge.weights[flow_index] <= challenge.max_weight { - current_entropy += challenge.weights[flow_index]; - current_temperature += challenge.values[flow_index] as i32; - - for &colored in &coloring { - current_temperature += challenge.interaction_values[flow_index][colored]; - } - coloring.push(flow_index); - } else { - uncolored.push(flow_index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for flow_index in 0..vertex_count { - mutation_rates[flow_index] = challenge.values[flow_index] as i32; - for &colored in &coloring { - mutation_rates[flow_index] += challenge.interaction_values[flow_index][colored]; - } - } - - let max_generations = 100; - let mut cooling_schedule = vec![0; vertex_count]; - - for _ in 0..max_generations { - let mut best_mutation = 0; - let mut best_crossover = None; - - for uncolored_index in 0..uncolored.len() { - let mutant = uncolored[uncolored_index]; - if cooling_schedule[mutant] > 0 { - continue; - } - - unsafe { - let mutant_fitness = *mutation_rates.get_unchecked(mutant); - let min_entropy_reduction = *challenge.weights.get_unchecked(mutant) as i32 - (challenge.max_weight as i32 - current_entropy as i32); - - if mutant_fitness < 0 { - continue; - } - - for colored_index in 0..coloring.len() { - let gene_to_remove = *coloring.get_unchecked(colored_index); - if *cooling_schedule.get_unchecked(gene_to_remove) > 0 { - continue; - } - - if min_entropy_reduction > 0 { - let removed_entropy = *challenge.weights.get_unchecked(gene_to_remove) as i32; - if removed_entropy < min_entropy_reduction { - continue; - } - } - - let fitness_change = mutant_fitness - *mutation_rates.get_unchecked(gene_to_remove) - - *challenge.interaction_values.get_unchecked(mutant).get_unchecked(gene_to_remove); - - if fitness_change > best_mutation { - best_mutation = fitness_change; - best_crossover = Some((uncolored_index, colored_index)); - } - } - } - } - - if let Some((uncolored_index, colored_index)) = best_crossover { - let gene_to_add = uncolored[uncolored_index]; - let gene_to_remove = coloring[colored_index]; - - coloring.swap_remove(colored_index); - uncolored.swap_remove(uncolored_index); - coloring.push(gene_to_add); - uncolored.push(gene_to_remove); - - current_temperature += best_mutation; - current_entropy = current_entropy + challenge.weights[gene_to_add] - challenge.weights[gene_to_remove]; - - unsafe { - for flow_index in 0..vertex_count { - *mutation_rates.get_unchecked_mut(flow_index) += - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_add) - - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_remove); - } - } - - cooling_schedule[gene_to_add] = 3; - cooling_schedule[gene_to_remove] = 3; - } else { - break; - } - - if current_temperature as u32 >= challenge.min_value { - return Ok(Some(Solution { items: coloring })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - } - - if current_temperature as u32 >= challenge.min_value { - Ok(Some(Solution { items: coloring })) - } else { - Ok(None) - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/classic_quadkp/mod.rs b/tig-algorithms/src/knapsack/classic_quadkp/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/knapsack/classic_quadkp/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/classic_quadkp/open_data.rs b/tig-algorithms/src/knapsack/classic_quadkp/open_data.rs deleted file mode 100644 index 5c7bc34..0000000 --- a/tig-algorithms/src/knapsack/classic_quadkp/open_data.rs +++ /dev/null @@ -1,167 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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 anyhow::Result; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut edge_costs: Vec<(usize, f32)> = (0..vertex_count) - .map(|flow_index| { - let total_flow = challenge.values[flow_index] as i32 + - challenge.interaction_values[flow_index].iter().sum::(); - let cost = total_flow as f32 / challenge.weights[flow_index] as f32; - (flow_index, cost) - }) - .collect(); - - edge_costs.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut coloring = Vec::with_capacity(vertex_count); - let mut uncolored = Vec::with_capacity(vertex_count); - let mut current_entropy = 0; - let mut current_temperature = 0; - - for &(flow_index, _) in &edge_costs { - if current_entropy + challenge.weights[flow_index] <= challenge.max_weight { - current_entropy += challenge.weights[flow_index]; - current_temperature += challenge.values[flow_index] as i32; - - for &colored in &coloring { - current_temperature += challenge.interaction_values[flow_index][colored]; - } - coloring.push(flow_index); - } else { - uncolored.push(flow_index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for flow_index in 0..vertex_count { - mutation_rates[flow_index] = challenge.values[flow_index] as i32; - for &colored in &coloring { - mutation_rates[flow_index] += challenge.interaction_values[flow_index][colored]; - } - } - - let max_generations = 100; - let mut cooling_schedule = vec![0; vertex_count]; - - for _ in 0..max_generations { - let mut best_mutation = 0; - let mut best_crossover = None; - - for uncolored_index in 0..uncolored.len() { - let mutant = uncolored[uncolored_index]; - if cooling_schedule[mutant] > 0 { - continue; - } - - unsafe { - let mutant_fitness = *mutation_rates.get_unchecked(mutant); - let min_entropy_reduction = *challenge.weights.get_unchecked(mutant) as i32 - (challenge.max_weight as i32 - current_entropy as i32); - - if mutant_fitness < 0 { - continue; - } - - for colored_index in 0..coloring.len() { - let gene_to_remove = *coloring.get_unchecked(colored_index); - if *cooling_schedule.get_unchecked(gene_to_remove) > 0 { - continue; - } - - if min_entropy_reduction > 0 { - let removed_entropy = *challenge.weights.get_unchecked(gene_to_remove) as i32; - if removed_entropy < min_entropy_reduction { - continue; - } - } - - let fitness_change = mutant_fitness - *mutation_rates.get_unchecked(gene_to_remove) - - *challenge.interaction_values.get_unchecked(mutant).get_unchecked(gene_to_remove); - - if fitness_change > best_mutation { - best_mutation = fitness_change; - best_crossover = Some((uncolored_index, colored_index)); - } - } - } - } - - if let Some((uncolored_index, colored_index)) = best_crossover { - let gene_to_add = uncolored[uncolored_index]; - let gene_to_remove = coloring[colored_index]; - - coloring.swap_remove(colored_index); - uncolored.swap_remove(uncolored_index); - coloring.push(gene_to_add); - uncolored.push(gene_to_remove); - - current_temperature += best_mutation; - current_entropy = current_entropy + challenge.weights[gene_to_add] - challenge.weights[gene_to_remove]; - - unsafe { - for flow_index in 0..vertex_count { - *mutation_rates.get_unchecked_mut(flow_index) += - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_add) - - challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_remove); - } - } - - cooling_schedule[gene_to_add] = 3; - cooling_schedule[gene_to_remove] = 3; - } else { - break; - } - - if current_temperature as u32 >= challenge.min_value { - return Ok(Some(Solution { items: coloring })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - } - - if current_temperature as u32 >= challenge.min_value { - Ok(Some(Solution { items: coloring })) - } else { - Ok(None) - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/dynamic/benchmarker_outbound.rs b/tig-algorithms/src/knapsack/dynamic/benchmarker_outbound.rs deleted file mode 100644 index f6b1570..0000000 --- a/tig-algorithms/src/knapsack/dynamic/benchmarker_outbound.rs +++ /dev/null @@ -1,95 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight; - let min_value = challenge.min_value; - let num_items = challenge.difficulty.num_items; - - // Sort items by value-to-weight ratio in descending order - let mut sorted_items: Vec = (0..num_items).collect(); - sorted_items.sort_by(|&a, &b| { - let ratio_a = challenge.values[a] as f64 / challenge.weights[a] as f64; - let ratio_b = challenge.values[b] as f64 / challenge.weights[b] as f64; - ratio_b.partial_cmp(&ratio_a).unwrap() - }); - - // Initialize combinations with a single empty combo - let mut combinations: Vec<(Vec, u32, u32)> = vec![(vec![false; num_items], 0, 0)]; - - let mut items = Vec::new(); - for &item in &sorted_items { - // Create new combos with the current item - let mut new_combinations: Vec<(Vec, u32, u32)> = combinations - .iter() - .map(|(combo, value, weight)| { - let mut new_combo = combo.clone(); - new_combo[item] = true; - ( - new_combo, - value + challenge.values[item], - weight + challenge.weights[item], - ) - }) - .filter(|&(_, _, weight)| weight <= max_weight) // Keep only combos within weight limit - .collect(); - - // Check if any new combination meets the minimum value requirement - if let Some((combo, _, _)) = new_combinations - .iter() - .find(|&&(_, value, _)| value >= min_value) - { - items = combo - .iter() - .enumerate() - .filter_map(|(i, &included)| if included { Some(i) } else { None }) - .collect(); - break; - } - - // Merge new_combinations with existing combinations - combinations.append(&mut new_combinations); - - // Deduplicate combinations by keeping the highest value for each weight - combinations.sort_by(|a, b| a.2.cmp(&b.2).then_with(|| b.1.cmp(&a.1))); // Sort by weight, then by value - combinations.dedup_by(|a, b| a.2 == b.2 && a.1 <= b.1); // Deduplicate by weight, keeping highest value - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/dynamic/commercial.rs b/tig-algorithms/src/knapsack/dynamic/commercial.rs deleted file mode 100644 index 5814371..0000000 --- a/tig-algorithms/src/knapsack/dynamic/commercial.rs +++ /dev/null @@ -1,95 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Commercial License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight; - let min_value = challenge.min_value; - let num_items = challenge.difficulty.num_items; - - // Sort items by value-to-weight ratio in descending order - let mut sorted_items: Vec = (0..num_items).collect(); - sorted_items.sort_by(|&a, &b| { - let ratio_a = challenge.values[a] as f64 / challenge.weights[a] as f64; - let ratio_b = challenge.values[b] as f64 / challenge.weights[b] as f64; - ratio_b.partial_cmp(&ratio_a).unwrap() - }); - - // Initialize combinations with a single empty combo - let mut combinations: Vec<(Vec, u32, u32)> = vec![(vec![false; num_items], 0, 0)]; - - let mut items = Vec::new(); - for &item in &sorted_items { - // Create new combos with the current item - let mut new_combinations: Vec<(Vec, u32, u32)> = combinations - .iter() - .map(|(combo, value, weight)| { - let mut new_combo = combo.clone(); - new_combo[item] = true; - ( - new_combo, - value + challenge.values[item], - weight + challenge.weights[item], - ) - }) - .filter(|&(_, _, weight)| weight <= max_weight) // Keep only combos within weight limit - .collect(); - - // Check if any new combination meets the minimum value requirement - if let Some((combo, _, _)) = new_combinations - .iter() - .find(|&&(_, value, _)| value >= min_value) - { - items = combo - .iter() - .enumerate() - .filter_map(|(i, &included)| if included { Some(i) } else { None }) - .collect(); - break; - } - - // Merge new_combinations with existing combinations - combinations.append(&mut new_combinations); - - // Deduplicate combinations by keeping the highest value for each weight - combinations.sort_by(|a, b| a.2.cmp(&b.2).then_with(|| b.1.cmp(&a.1))); // Sort by weight, then by value - combinations.dedup_by(|a, b| a.2 == b.2 && a.1 <= b.1); // Deduplicate by weight, keeping highest value - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/dynamic/inbound.rs b/tig-algorithms/src/knapsack/dynamic/inbound.rs deleted file mode 100644 index 216e1c9..0000000 --- a/tig-algorithms/src/knapsack/dynamic/inbound.rs +++ /dev/null @@ -1,95 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight; - let min_value = challenge.min_value; - let num_items = challenge.difficulty.num_items; - - // Sort items by value-to-weight ratio in descending order - let mut sorted_items: Vec = (0..num_items).collect(); - sorted_items.sort_by(|&a, &b| { - let ratio_a = challenge.values[a] as f64 / challenge.weights[a] as f64; - let ratio_b = challenge.values[b] as f64 / challenge.weights[b] as f64; - ratio_b.partial_cmp(&ratio_a).unwrap() - }); - - // Initialize combinations with a single empty combo - let mut combinations: Vec<(Vec, u32, u32)> = vec![(vec![false; num_items], 0, 0)]; - - let mut items = Vec::new(); - for &item in &sorted_items { - // Create new combos with the current item - let mut new_combinations: Vec<(Vec, u32, u32)> = combinations - .iter() - .map(|(combo, value, weight)| { - let mut new_combo = combo.clone(); - new_combo[item] = true; - ( - new_combo, - value + challenge.values[item], - weight + challenge.weights[item], - ) - }) - .filter(|&(_, _, weight)| weight <= max_weight) // Keep only combos within weight limit - .collect(); - - // Check if any new combination meets the minimum value requirement - if let Some((combo, _, _)) = new_combinations - .iter() - .find(|&&(_, value, _)| value >= min_value) - { - items = combo - .iter() - .enumerate() - .filter_map(|(i, &included)| if included { Some(i) } else { None }) - .collect(); - break; - } - - // Merge new_combinations with existing combinations - combinations.append(&mut new_combinations); - - // Deduplicate combinations by keeping the highest value for each weight - combinations.sort_by(|a, b| a.2.cmp(&b.2).then_with(|| b.1.cmp(&a.1))); // Sort by weight, then by value - combinations.dedup_by(|a, b| a.2 == b.2 && a.1 <= b.1); // Deduplicate by weight, keeping highest value - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/dynamic/innovator_outbound.rs b/tig-algorithms/src/knapsack/dynamic/innovator_outbound.rs deleted file mode 100644 index 9889e0a..0000000 --- a/tig-algorithms/src/knapsack/dynamic/innovator_outbound.rs +++ /dev/null @@ -1,95 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight; - let min_value = challenge.min_value; - let num_items = challenge.difficulty.num_items; - - // Sort items by value-to-weight ratio in descending order - let mut sorted_items: Vec = (0..num_items).collect(); - sorted_items.sort_by(|&a, &b| { - let ratio_a = challenge.values[a] as f64 / challenge.weights[a] as f64; - let ratio_b = challenge.values[b] as f64 / challenge.weights[b] as f64; - ratio_b.partial_cmp(&ratio_a).unwrap() - }); - - // Initialize combinations with a single empty combo - let mut combinations: Vec<(Vec, u32, u32)> = vec![(vec![false; num_items], 0, 0)]; - - let mut items = Vec::new(); - for &item in &sorted_items { - // Create new combos with the current item - let mut new_combinations: Vec<(Vec, u32, u32)> = combinations - .iter() - .map(|(combo, value, weight)| { - let mut new_combo = combo.clone(); - new_combo[item] = true; - ( - new_combo, - value + challenge.values[item], - weight + challenge.weights[item], - ) - }) - .filter(|&(_, _, weight)| weight <= max_weight) // Keep only combos within weight limit - .collect(); - - // Check if any new combination meets the minimum value requirement - if let Some((combo, _, _)) = new_combinations - .iter() - .find(|&&(_, value, _)| value >= min_value) - { - items = combo - .iter() - .enumerate() - .filter_map(|(i, &included)| if included { Some(i) } else { None }) - .collect(); - break; - } - - // Merge new_combinations with existing combinations - combinations.append(&mut new_combinations); - - // Deduplicate combinations by keeping the highest value for each weight - combinations.sort_by(|a, b| a.2.cmp(&b.2).then_with(|| b.1.cmp(&a.1))); // Sort by weight, then by value - combinations.dedup_by(|a, b| a.2 == b.2 && a.1 <= b.1); // Deduplicate by weight, keeping highest value - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/dynamic/mod.rs b/tig-algorithms/src/knapsack/dynamic/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/knapsack/dynamic/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/dynamic/open_data.rs b/tig-algorithms/src/knapsack/dynamic/open_data.rs deleted file mode 100644 index 9098047..0000000 --- a/tig-algorithms/src/knapsack/dynamic/open_data.rs +++ /dev/null @@ -1,95 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Open Data 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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight; - let min_value = challenge.min_value; - let num_items = challenge.difficulty.num_items; - - // Sort items by value-to-weight ratio in descending order - let mut sorted_items: Vec = (0..num_items).collect(); - sorted_items.sort_by(|&a, &b| { - let ratio_a = challenge.values[a] as f64 / challenge.weights[a] as f64; - let ratio_b = challenge.values[b] as f64 / challenge.weights[b] as f64; - ratio_b.partial_cmp(&ratio_a).unwrap() - }); - - // Initialize combinations with a single empty combo - let mut combinations: Vec<(Vec, u32, u32)> = vec![(vec![false; num_items], 0, 0)]; - - let mut items = Vec::new(); - for &item in &sorted_items { - // Create new combos with the current item - let mut new_combinations: Vec<(Vec, u32, u32)> = combinations - .iter() - .map(|(combo, value, weight)| { - let mut new_combo = combo.clone(); - new_combo[item] = true; - ( - new_combo, - value + challenge.values[item], - weight + challenge.weights[item], - ) - }) - .filter(|&(_, _, weight)| weight <= max_weight) // Keep only combos within weight limit - .collect(); - - // Check if any new combination meets the minimum value requirement - if let Some((combo, _, _)) = new_combinations - .iter() - .find(|&&(_, value, _)| value >= min_value) - { - items = combo - .iter() - .enumerate() - .filter_map(|(i, &included)| if included { Some(i) } else { None }) - .collect(); - break; - } - - // Merge new_combinations with existing combinations - combinations.append(&mut new_combinations); - - // Deduplicate combinations by keeping the highest value for each weight - combinations.sort_by(|a, b| a.2.cmp(&b.2).then_with(|| b.1.cmp(&a.1))); // Sort by weight, then by value - combinations.dedup_by(|a, b| a.2 == b.2 && a.1 <= b.1); // Deduplicate by weight, keeping highest value - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/knapheudp/benchmarker_outbound.rs b/tig-algorithms/src/knapsack/knapheudp/benchmarker_outbound.rs deleted file mode 100644 index 863a240..0000000 --- a/tig-algorithms/src/knapsack/knapheudp/benchmarker_outbound.rs +++ /dev/null @@ -1,110 +0,0 @@ -/*! -Copyright 2024 AllFather - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); - let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let mut dp = vec![0; max_weight + 1]; - let mut selected = vec![vec![false; max_weight + 1]; num_items]; - - for (i, &(item_index, _)) in sorted_items.iter().enumerate() { - let weight = weights[item_index]; - let value = values[item_index]; - - for w in (weight..=max_weight).rev() { - let new_value = dp[w - weight] + value; - if new_value > dp[w] { - dp[w] = new_value; - selected[i][w] = true; - } - } - - if dp[max_weight] >= min_value { - break; - } - } - - if dp[max_weight] < min_value { - return Ok(None); - } - - let mut items = Vec::new(); - let mut w = max_weight; - for i in (0..num_items).rev() { - if selected[i][w] { - let item_index = sorted_items[i].0; - items.push(item_index); - w -= weights[item_index]; - } - if w == 0 { - break; - } - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/knapheudp/commercial.rs b/tig-algorithms/src/knapsack/knapheudp/commercial.rs deleted file mode 100644 index 49af535..0000000 --- a/tig-algorithms/src/knapsack/knapheudp/commercial.rs +++ /dev/null @@ -1,110 +0,0 @@ -/*! -Copyright 2024 AllFather - -Licensed under the TIG Commercial License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); - let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let mut dp = vec![0; max_weight + 1]; - let mut selected = vec![vec![false; max_weight + 1]; num_items]; - - for (i, &(item_index, _)) in sorted_items.iter().enumerate() { - let weight = weights[item_index]; - let value = values[item_index]; - - for w in (weight..=max_weight).rev() { - let new_value = dp[w - weight] + value; - if new_value > dp[w] { - dp[w] = new_value; - selected[i][w] = true; - } - } - - if dp[max_weight] >= min_value { - break; - } - } - - if dp[max_weight] < min_value { - return Ok(None); - } - - let mut items = Vec::new(); - let mut w = max_weight; - for i in (0..num_items).rev() { - if selected[i][w] { - let item_index = sorted_items[i].0; - items.push(item_index); - w -= weights[item_index]; - } - if w == 0 { - break; - } - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/knapheudp/inbound.rs b/tig-algorithms/src/knapsack/knapheudp/inbound.rs deleted file mode 100644 index 7a516c2..0000000 --- a/tig-algorithms/src/knapsack/knapheudp/inbound.rs +++ /dev/null @@ -1,110 +0,0 @@ -/*! -Copyright 2024 AllFather - -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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); - let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let mut dp = vec![0; max_weight + 1]; - let mut selected = vec![vec![false; max_weight + 1]; num_items]; - - for (i, &(item_index, _)) in sorted_items.iter().enumerate() { - let weight = weights[item_index]; - let value = values[item_index]; - - for w in (weight..=max_weight).rev() { - let new_value = dp[w - weight] + value; - if new_value > dp[w] { - dp[w] = new_value; - selected[i][w] = true; - } - } - - if dp[max_weight] >= min_value { - break; - } - } - - if dp[max_weight] < min_value { - return Ok(None); - } - - let mut items = Vec::new(); - let mut w = max_weight; - for i in (0..num_items).rev() { - if selected[i][w] { - let item_index = sorted_items[i].0; - items.push(item_index); - w -= weights[item_index]; - } - if w == 0 { - break; - } - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/knapheudp/innovator_outbound.rs b/tig-algorithms/src/knapsack/knapheudp/innovator_outbound.rs deleted file mode 100644 index 927bd2b..0000000 --- a/tig-algorithms/src/knapsack/knapheudp/innovator_outbound.rs +++ /dev/null @@ -1,110 +0,0 @@ -/*! -Copyright 2024 AllFather - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); - let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let mut dp = vec![0; max_weight + 1]; - let mut selected = vec![vec![false; max_weight + 1]; num_items]; - - for (i, &(item_index, _)) in sorted_items.iter().enumerate() { - let weight = weights[item_index]; - let value = values[item_index]; - - for w in (weight..=max_weight).rev() { - let new_value = dp[w - weight] + value; - if new_value > dp[w] { - dp[w] = new_value; - selected[i][w] = true; - } - } - - if dp[max_weight] >= min_value { - break; - } - } - - if dp[max_weight] < min_value { - return Ok(None); - } - - let mut items = Vec::new(); - let mut w = max_weight; - for i in (0..num_items).rev() { - if selected[i][w] { - let item_index = sorted_items[i].0; - items.push(item_index); - w -= weights[item_index]; - } - if w == 0 { - break; - } - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/knapheudp/mod.rs b/tig-algorithms/src/knapsack/knapheudp/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/knapsack/knapheudp/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/knapheudp/open_data.rs b/tig-algorithms/src/knapsack/knapheudp/open_data.rs deleted file mode 100644 index f9a8ce3..0000000 --- a/tig-algorithms/src/knapsack/knapheudp/open_data.rs +++ /dev/null @@ -1,110 +0,0 @@ -/*! -Copyright 2024 AllFather - -Licensed under the TIG Open Data 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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); - let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let mut dp = vec![0; max_weight + 1]; - let mut selected = vec![vec![false; max_weight + 1]; num_items]; - - for (i, &(item_index, _)) in sorted_items.iter().enumerate() { - let weight = weights[item_index]; - let value = values[item_index]; - - for w in (weight..=max_weight).rev() { - let new_value = dp[w - weight] + value; - if new_value > dp[w] { - dp[w] = new_value; - selected[i][w] = true; - } - } - - if dp[max_weight] >= min_value { - break; - } - } - - if dp[max_weight] < min_value { - return Ok(None); - } - - let mut items = Vec::new(); - let mut w = max_weight; - for i in (0..num_items).rev() { - if selected[i][w] { - let item_index = sorted_items[i].0; - items.push(item_index); - w -= weights[item_index]; - } - if w == 0 { - break; - } - } - - Ok(Some(Solution { items })) -} - -#[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/knapsack/knapmaxxing/benchmarker_outbound.rs b/tig-algorithms/src/knapsack/knapmaxxing/benchmarker_outbound.rs deleted file mode 100644 index 86f9f7f..0000000 --- a/tig-algorithms/src/knapsack/knapmaxxing/benchmarker_outbound.rs +++ /dev/null @@ -1,115 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let max_weight_plus_one = max_weight + 1; - - let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); - let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let num_states = (num_items + 1) * (max_weight_plus_one); - let mut dp = vec![0; num_states]; - - for i in 1..=num_items { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; - let i_times_max_weight_plus_one = i * max_weight_plus_one; - for w in (item_weight..=max_weight).rev() { - let prev_state = i_minus_one_times_max_weight_plus_one + w; - let curr_state = i_times_max_weight_plus_one + w; - dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); - } - } - - let mut items = Vec::with_capacity(num_items); - let mut i = num_items; - let mut w = max_weight; - let mut total_value = 0; - while i > 0 && total_value < min_value { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let prev_state = (i - 1) * (max_weight_plus_one) + w; - let curr_state = i * (max_weight_plus_one) + w; - if dp[curr_state] != dp[prev_state] { - items.push(item_index); - w -= item_weight; - total_value += item_value; - } - i -= 1; - } - - if total_value >= min_value { - Ok(Some(Solution { items })) - } else { - Ok(None) - } -} - -#[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/knapsack/knapmaxxing/commercial.rs b/tig-algorithms/src/knapsack/knapmaxxing/commercial.rs deleted file mode 100644 index 9b4962b..0000000 --- a/tig-algorithms/src/knapsack/knapmaxxing/commercial.rs +++ /dev/null @@ -1,115 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Commercial License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let max_weight_plus_one = max_weight + 1; - - let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); - let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let num_states = (num_items + 1) * (max_weight_plus_one); - let mut dp = vec![0; num_states]; - - for i in 1..=num_items { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; - let i_times_max_weight_plus_one = i * max_weight_plus_one; - for w in (item_weight..=max_weight).rev() { - let prev_state = i_minus_one_times_max_weight_plus_one + w; - let curr_state = i_times_max_weight_plus_one + w; - dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); - } - } - - let mut items = Vec::with_capacity(num_items); - let mut i = num_items; - let mut w = max_weight; - let mut total_value = 0; - while i > 0 && total_value < min_value { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let prev_state = (i - 1) * (max_weight_plus_one) + w; - let curr_state = i * (max_weight_plus_one) + w; - if dp[curr_state] != dp[prev_state] { - items.push(item_index); - w -= item_weight; - total_value += item_value; - } - i -= 1; - } - - if total_value >= min_value { - Ok(Some(Solution { items })) - } else { - Ok(None) - } -} - -#[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/knapsack/knapmaxxing/inbound.rs b/tig-algorithms/src/knapsack/knapmaxxing/inbound.rs deleted file mode 100644 index 7fd304a..0000000 --- a/tig-algorithms/src/knapsack/knapmaxxing/inbound.rs +++ /dev/null @@ -1,115 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let max_weight_plus_one = max_weight + 1; - - let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); - let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let num_states = (num_items + 1) * (max_weight_plus_one); - let mut dp = vec![0; num_states]; - - for i in 1..=num_items { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; - let i_times_max_weight_plus_one = i * max_weight_plus_one; - for w in (item_weight..=max_weight).rev() { - let prev_state = i_minus_one_times_max_weight_plus_one + w; - let curr_state = i_times_max_weight_plus_one + w; - dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); - } - } - - let mut items = Vec::with_capacity(num_items); - let mut i = num_items; - let mut w = max_weight; - let mut total_value = 0; - while i > 0 && total_value < min_value { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let prev_state = (i - 1) * (max_weight_plus_one) + w; - let curr_state = i * (max_weight_plus_one) + w; - if dp[curr_state] != dp[prev_state] { - items.push(item_index); - w -= item_weight; - total_value += item_value; - } - i -= 1; - } - - if total_value >= min_value { - Ok(Some(Solution { items })) - } else { - Ok(None) - } -} - -#[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/knapsack/knapmaxxing/innovator_outbound.rs b/tig-algorithms/src/knapsack/knapmaxxing/innovator_outbound.rs deleted file mode 100644 index a730bb6..0000000 --- a/tig-algorithms/src/knapsack/knapmaxxing/innovator_outbound.rs +++ /dev/null @@ -1,115 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let max_weight_plus_one = max_weight + 1; - - let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); - let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let num_states = (num_items + 1) * (max_weight_plus_one); - let mut dp = vec![0; num_states]; - - for i in 1..=num_items { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; - let i_times_max_weight_plus_one = i * max_weight_plus_one; - for w in (item_weight..=max_weight).rev() { - let prev_state = i_minus_one_times_max_weight_plus_one + w; - let curr_state = i_times_max_weight_plus_one + w; - dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); - } - } - - let mut items = Vec::with_capacity(num_items); - let mut i = num_items; - let mut w = max_weight; - let mut total_value = 0; - while i > 0 && total_value < min_value { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let prev_state = (i - 1) * (max_weight_plus_one) + w; - let curr_state = i * (max_weight_plus_one) + w; - if dp[curr_state] != dp[prev_state] { - items.push(item_index); - w -= item_weight; - total_value += item_value; - } - i -= 1; - } - - if total_value >= min_value { - Ok(Some(Solution { items })) - } else { - Ok(None) - } -} - -#[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/knapsack/knapmaxxing/mod.rs b/tig-algorithms/src/knapsack/knapmaxxing/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/knapsack/knapmaxxing/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/knapmaxxing/open_data.rs b/tig-algorithms/src/knapsack/knapmaxxing/open_data.rs deleted file mode 100644 index e2849d9..0000000 --- a/tig-algorithms/src/knapsack/knapmaxxing/open_data.rs +++ /dev/null @@ -1,115 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Open Data 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 tig_challenges::knapsack::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_weight = challenge.max_weight as usize; - let min_value = challenge.min_value as usize; - let num_items = challenge.difficulty.num_items; - - let max_weight_plus_one = max_weight + 1; - - let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); - let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); - - let mut sorted_items: Vec<(usize, f64)> = (0..num_items) - .map(|i| (i, values[i] as f64 / weights[i] as f64)) - .collect(); - sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut upper_bound = 0; - let mut remaining_weight = max_weight; - for &(item_index, ratio) in &sorted_items { - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - if item_weight <= remaining_weight { - upper_bound += item_value; - remaining_weight -= item_weight; - } else { - upper_bound += (ratio * remaining_weight as f64).floor() as usize; - break; - } - } - - if upper_bound < min_value { - return Ok(None); - } - - let num_states = (num_items + 1) * (max_weight_plus_one); - let mut dp = vec![0; num_states]; - - for i in 1..=num_items { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; - let i_times_max_weight_plus_one = i * max_weight_plus_one; - for w in (item_weight..=max_weight).rev() { - let prev_state = i_minus_one_times_max_weight_plus_one + w; - let curr_state = i_times_max_weight_plus_one + w; - dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); - } - } - - let mut items = Vec::with_capacity(num_items); - let mut i = num_items; - let mut w = max_weight; - let mut total_value = 0; - while i > 0 && total_value < min_value { - let (item_index, _) = sorted_items[i - 1]; - let item_weight = weights[item_index]; - let item_value = values[item_index]; - - let prev_state = (i - 1) * (max_weight_plus_one) + w; - let curr_state = i * (max_weight_plus_one) + w; - if dp[curr_state] != dp[prev_state] { - items.push(item_index); - w -= item_weight; - total_value += item_value; - } - i -= 1; - } - - if total_value >= min_value { - Ok(Some(Solution { items })) - } else { - Ok(None) - } -} - -#[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/knapsack/mod.rs b/tig-algorithms/src/knapsack/mod.rs index 48e0a93..33ddf59 100644 --- a/tig-algorithms/src/knapsack/mod.rs +++ b/tig-algorithms/src/knapsack/mod.rs @@ -1,5 +1,4 @@ -pub mod dynamic; -pub use dynamic as c003_a001; +// c003_a001 // c003_a002 @@ -11,8 +10,7 @@ pub use dynamic as c003_a001; // c003_a006 -pub mod knapmaxxing; -pub use knapmaxxing as c003_a007; +// c003_a007 // c003_a008 @@ -36,8 +34,7 @@ pub use knapmaxxing as c003_a007; // c003_a018 -pub mod knapheudp; -pub use knapheudp as c003_a019; +// c003_a019 // c003_a020 @@ -101,15 +98,13 @@ pub use knapheudp as c003_a019; // c003_a050 -pub mod classic_quadkp; -pub use classic_quadkp as c003_a051; +// c003_a051 // c003_a052 // c003_a053 -pub mod quadkp_improved; -pub use quadkp_improved as c003_a054; +// c003_a054 // c003_a055 diff --git a/tig-algorithms/src/knapsack/quadkp_improved/benchmarker_outbound.rs b/tig-algorithms/src/knapsack/quadkp_improved/benchmarker_outbound.rs deleted file mode 100644 index f3b2d5c..0000000 --- a/tig-algorithms/src/knapsack/quadkp_improved/benchmarker_outbound.rs +++ /dev/null @@ -1,188 +0,0 @@ -/*! -Copyright 2024 Rootz - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use rand::{SeedableRng, Rng, rngs::StdRng}; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut item_scores: Vec<(usize, f32)> = (0..vertex_count) - .map(|index| { - let interaction_sum: i32 = challenge.interaction_values[index].iter().sum(); - let secondary_score = challenge.values[index] as f32 / challenge.weights[index] as f32; - let combined_score = (challenge.values[index] as f32 * 0.75 + interaction_sum as f32 * 0.15 + secondary_score * 0.1) - / challenge.weights[index] as f32; - (index, combined_score) - }) - .collect(); - - item_scores.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut selected_items = Vec::with_capacity(vertex_count); - let mut unselected_items = Vec::with_capacity(vertex_count); - let mut current_weight = 0; - let mut current_value = 0; - - for &(index, _) in &item_scores { - if current_weight + challenge.weights[index] <= challenge.max_weight { - current_weight += challenge.weights[index]; - current_value += challenge.values[index] as i32; - - for &selected in &selected_items { - current_value += challenge.interaction_values[index][selected]; - } - selected_items.push(index); - } else { - unselected_items.push(index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for index in 0..vertex_count { - mutation_rates[index] = challenge.values[index] as i32; - for &selected in &selected_items { - mutation_rates[index] += challenge.interaction_values[index][selected]; - } - } - - let max_generations = 150; - let mut cooling_schedule = vec![0; vertex_count]; - let mut rng = StdRng::seed_from_u64(challenge.seed[0] as u64); - - for generation in 0..max_generations { - let mut best_gain = 0; - let mut best_swap = None; - - for (u_index, &mutant) in unselected_items.iter().enumerate() { - if cooling_schedule[mutant] > 0 { - continue; - } - - let mutant_fitness = mutation_rates[mutant]; - let extra_weight = challenge.weights[mutant] as i32 - (challenge.max_weight as i32 - current_weight as i32); - - if mutant_fitness < 0 { - continue; - } - - for (c_index, &selected) in selected_items.iter().enumerate() { - if cooling_schedule[selected] > 0 { - continue; - } - - if extra_weight > 0 && (challenge.weights[selected] as i32) < extra_weight { - continue; - } - - let interaction_penalty = (challenge.interaction_values[mutant][selected] as f32 * 0.3) as i32; - let fitness_gain = mutant_fitness - mutation_rates[selected] - interaction_penalty; - - if fitness_gain > best_gain { - best_gain = fitness_gain; - best_swap = Some((u_index, c_index)); - } - } - } - - if let Some((u_index, c_index)) = best_swap { - let added_item = unselected_items[u_index]; - let removed_item = selected_items[c_index]; - - selected_items.swap_remove(c_index); - unselected_items.swap_remove(u_index); - selected_items.push(added_item); - unselected_items.push(removed_item); - - current_value += best_gain; - current_weight = current_weight + challenge.weights[added_item] - challenge.weights[removed_item]; - - if current_weight > challenge.max_weight { - continue; - } - - for index in 0..vertex_count { - mutation_rates[index] += challenge.interaction_values[index][added_item] - - challenge.interaction_values[index][removed_item]; - } - - cooling_schedule[added_item] = 3; - cooling_schedule[removed_item] = 3; - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - - if current_value as u32 > (challenge.min_value * 9 / 10) { - let high_potential_items: Vec = unselected_items - .iter() - .filter(|&&i| challenge.values[i] as i32 > (challenge.min_value as i32 / 4)) - .copied() - .collect(); - - for &item in high_potential_items.iter().take(2) { - if current_weight + challenge.weights[item] <= challenge.max_weight { - selected_items.push(item); - unselected_items.retain(|&x| x != item); - current_weight += challenge.weights[item]; - current_value += challenge.values[item] as i32; - - for &selected in &selected_items { - if selected != item { - current_value += challenge.interaction_values[item][selected]; - } - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - } - } - } - } - - if current_value as u32 >= challenge.min_value && current_weight <= challenge.max_weight { - Ok(Some(Solution { items: selected_items })) - } else { - Ok(None) - } -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - - pub const KERNEL: Option = None; - - 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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/quadkp_improved/commercial.rs b/tig-algorithms/src/knapsack/quadkp_improved/commercial.rs deleted file mode 100644 index 6d7cbab..0000000 --- a/tig-algorithms/src/knapsack/quadkp_improved/commercial.rs +++ /dev/null @@ -1,188 +0,0 @@ -/*! -Copyright 2024 Rootz - -Licensed under the TIG Commercial License v1.0 (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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use rand::{SeedableRng, Rng, rngs::StdRng}; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut item_scores: Vec<(usize, f32)> = (0..vertex_count) - .map(|index| { - let interaction_sum: i32 = challenge.interaction_values[index].iter().sum(); - let secondary_score = challenge.values[index] as f32 / challenge.weights[index] as f32; - let combined_score = (challenge.values[index] as f32 * 0.75 + interaction_sum as f32 * 0.15 + secondary_score * 0.1) - / challenge.weights[index] as f32; - (index, combined_score) - }) - .collect(); - - item_scores.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut selected_items = Vec::with_capacity(vertex_count); - let mut unselected_items = Vec::with_capacity(vertex_count); - let mut current_weight = 0; - let mut current_value = 0; - - for &(index, _) in &item_scores { - if current_weight + challenge.weights[index] <= challenge.max_weight { - current_weight += challenge.weights[index]; - current_value += challenge.values[index] as i32; - - for &selected in &selected_items { - current_value += challenge.interaction_values[index][selected]; - } - selected_items.push(index); - } else { - unselected_items.push(index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for index in 0..vertex_count { - mutation_rates[index] = challenge.values[index] as i32; - for &selected in &selected_items { - mutation_rates[index] += challenge.interaction_values[index][selected]; - } - } - - let max_generations = 150; - let mut cooling_schedule = vec![0; vertex_count]; - let mut rng = StdRng::seed_from_u64(challenge.seed[0] as u64); - - for generation in 0..max_generations { - let mut best_gain = 0; - let mut best_swap = None; - - for (u_index, &mutant) in unselected_items.iter().enumerate() { - if cooling_schedule[mutant] > 0 { - continue; - } - - let mutant_fitness = mutation_rates[mutant]; - let extra_weight = challenge.weights[mutant] as i32 - (challenge.max_weight as i32 - current_weight as i32); - - if mutant_fitness < 0 { - continue; - } - - for (c_index, &selected) in selected_items.iter().enumerate() { - if cooling_schedule[selected] > 0 { - continue; - } - - if extra_weight > 0 && (challenge.weights[selected] as i32) < extra_weight { - continue; - } - - let interaction_penalty = (challenge.interaction_values[mutant][selected] as f32 * 0.3) as i32; - let fitness_gain = mutant_fitness - mutation_rates[selected] - interaction_penalty; - - if fitness_gain > best_gain { - best_gain = fitness_gain; - best_swap = Some((u_index, c_index)); - } - } - } - - if let Some((u_index, c_index)) = best_swap { - let added_item = unselected_items[u_index]; - let removed_item = selected_items[c_index]; - - selected_items.swap_remove(c_index); - unselected_items.swap_remove(u_index); - selected_items.push(added_item); - unselected_items.push(removed_item); - - current_value += best_gain; - current_weight = current_weight + challenge.weights[added_item] - challenge.weights[removed_item]; - - if current_weight > challenge.max_weight { - continue; - } - - for index in 0..vertex_count { - mutation_rates[index] += challenge.interaction_values[index][added_item] - - challenge.interaction_values[index][removed_item]; - } - - cooling_schedule[added_item] = 3; - cooling_schedule[removed_item] = 3; - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - - if current_value as u32 > (challenge.min_value * 9 / 10) { - let high_potential_items: Vec = unselected_items - .iter() - .filter(|&&i| challenge.values[i] as i32 > (challenge.min_value as i32 / 4)) - .copied() - .collect(); - - for &item in high_potential_items.iter().take(2) { - if current_weight + challenge.weights[item] <= challenge.max_weight { - selected_items.push(item); - unselected_items.retain(|&x| x != item); - current_weight += challenge.weights[item]; - current_value += challenge.values[item] as i32; - - for &selected in &selected_items { - if selected != item { - current_value += challenge.interaction_values[item][selected]; - } - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - } - } - } - } - - if current_value as u32 >= challenge.min_value && current_weight <= challenge.max_weight { - Ok(Some(Solution { items: selected_items })) - } else { - Ok(None) - } -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - - pub const KERNEL: Option = None; - - 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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/quadkp_improved/inbound.rs b/tig-algorithms/src/knapsack/quadkp_improved/inbound.rs deleted file mode 100644 index d82bd71..0000000 --- a/tig-algorithms/src/knapsack/quadkp_improved/inbound.rs +++ /dev/null @@ -1,188 +0,0 @@ -/*! -Copyright 2024 Rootz - -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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use rand::{SeedableRng, Rng, rngs::StdRng}; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut item_scores: Vec<(usize, f32)> = (0..vertex_count) - .map(|index| { - let interaction_sum: i32 = challenge.interaction_values[index].iter().sum(); - let secondary_score = challenge.values[index] as f32 / challenge.weights[index] as f32; - let combined_score = (challenge.values[index] as f32 * 0.75 + interaction_sum as f32 * 0.15 + secondary_score * 0.1) - / challenge.weights[index] as f32; - (index, combined_score) - }) - .collect(); - - item_scores.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut selected_items = Vec::with_capacity(vertex_count); - let mut unselected_items = Vec::with_capacity(vertex_count); - let mut current_weight = 0; - let mut current_value = 0; - - for &(index, _) in &item_scores { - if current_weight + challenge.weights[index] <= challenge.max_weight { - current_weight += challenge.weights[index]; - current_value += challenge.values[index] as i32; - - for &selected in &selected_items { - current_value += challenge.interaction_values[index][selected]; - } - selected_items.push(index); - } else { - unselected_items.push(index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for index in 0..vertex_count { - mutation_rates[index] = challenge.values[index] as i32; - for &selected in &selected_items { - mutation_rates[index] += challenge.interaction_values[index][selected]; - } - } - - let max_generations = 150; - let mut cooling_schedule = vec![0; vertex_count]; - let mut rng = StdRng::seed_from_u64(challenge.seed[0] as u64); - - for generation in 0..max_generations { - let mut best_gain = 0; - let mut best_swap = None; - - for (u_index, &mutant) in unselected_items.iter().enumerate() { - if cooling_schedule[mutant] > 0 { - continue; - } - - let mutant_fitness = mutation_rates[mutant]; - let extra_weight = challenge.weights[mutant] as i32 - (challenge.max_weight as i32 - current_weight as i32); - - if mutant_fitness < 0 { - continue; - } - - for (c_index, &selected) in selected_items.iter().enumerate() { - if cooling_schedule[selected] > 0 { - continue; - } - - if extra_weight > 0 && (challenge.weights[selected] as i32) < extra_weight { - continue; - } - - let interaction_penalty = (challenge.interaction_values[mutant][selected] as f32 * 0.3) as i32; - let fitness_gain = mutant_fitness - mutation_rates[selected] - interaction_penalty; - - if fitness_gain > best_gain { - best_gain = fitness_gain; - best_swap = Some((u_index, c_index)); - } - } - } - - if let Some((u_index, c_index)) = best_swap { - let added_item = unselected_items[u_index]; - let removed_item = selected_items[c_index]; - - selected_items.swap_remove(c_index); - unselected_items.swap_remove(u_index); - selected_items.push(added_item); - unselected_items.push(removed_item); - - current_value += best_gain; - current_weight = current_weight + challenge.weights[added_item] - challenge.weights[removed_item]; - - if current_weight > challenge.max_weight { - continue; - } - - for index in 0..vertex_count { - mutation_rates[index] += challenge.interaction_values[index][added_item] - - challenge.interaction_values[index][removed_item]; - } - - cooling_schedule[added_item] = 3; - cooling_schedule[removed_item] = 3; - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - - if current_value as u32 > (challenge.min_value * 9 / 10) { - let high_potential_items: Vec = unselected_items - .iter() - .filter(|&&i| challenge.values[i] as i32 > (challenge.min_value as i32 / 4)) - .copied() - .collect(); - - for &item in high_potential_items.iter().take(2) { - if current_weight + challenge.weights[item] <= challenge.max_weight { - selected_items.push(item); - unselected_items.retain(|&x| x != item); - current_weight += challenge.weights[item]; - current_value += challenge.values[item] as i32; - - for &selected in &selected_items { - if selected != item { - current_value += challenge.interaction_values[item][selected]; - } - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - } - } - } - } - - if current_value as u32 >= challenge.min_value && current_weight <= challenge.max_weight { - Ok(Some(Solution { items: selected_items })) - } else { - Ok(None) - } -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - - pub const KERNEL: Option = None; - - 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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/quadkp_improved/innovator_outbound.rs b/tig-algorithms/src/knapsack/quadkp_improved/innovator_outbound.rs deleted file mode 100644 index 8b43c0e..0000000 --- a/tig-algorithms/src/knapsack/quadkp_improved/innovator_outbound.rs +++ /dev/null @@ -1,188 +0,0 @@ -/*! -Copyright 2024 Rootz - -Licensed under the TIG Innovator Outbound Game License v1.0 (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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use rand::{SeedableRng, Rng, rngs::StdRng}; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut item_scores: Vec<(usize, f32)> = (0..vertex_count) - .map(|index| { - let interaction_sum: i32 = challenge.interaction_values[index].iter().sum(); - let secondary_score = challenge.values[index] as f32 / challenge.weights[index] as f32; - let combined_score = (challenge.values[index] as f32 * 0.75 + interaction_sum as f32 * 0.15 + secondary_score * 0.1) - / challenge.weights[index] as f32; - (index, combined_score) - }) - .collect(); - - item_scores.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut selected_items = Vec::with_capacity(vertex_count); - let mut unselected_items = Vec::with_capacity(vertex_count); - let mut current_weight = 0; - let mut current_value = 0; - - for &(index, _) in &item_scores { - if current_weight + challenge.weights[index] <= challenge.max_weight { - current_weight += challenge.weights[index]; - current_value += challenge.values[index] as i32; - - for &selected in &selected_items { - current_value += challenge.interaction_values[index][selected]; - } - selected_items.push(index); - } else { - unselected_items.push(index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for index in 0..vertex_count { - mutation_rates[index] = challenge.values[index] as i32; - for &selected in &selected_items { - mutation_rates[index] += challenge.interaction_values[index][selected]; - } - } - - let max_generations = 150; - let mut cooling_schedule = vec![0; vertex_count]; - let mut rng = StdRng::seed_from_u64(challenge.seed[0] as u64); - - for generation in 0..max_generations { - let mut best_gain = 0; - let mut best_swap = None; - - for (u_index, &mutant) in unselected_items.iter().enumerate() { - if cooling_schedule[mutant] > 0 { - continue; - } - - let mutant_fitness = mutation_rates[mutant]; - let extra_weight = challenge.weights[mutant] as i32 - (challenge.max_weight as i32 - current_weight as i32); - - if mutant_fitness < 0 { - continue; - } - - for (c_index, &selected) in selected_items.iter().enumerate() { - if cooling_schedule[selected] > 0 { - continue; - } - - if extra_weight > 0 && (challenge.weights[selected] as i32) < extra_weight { - continue; - } - - let interaction_penalty = (challenge.interaction_values[mutant][selected] as f32 * 0.3) as i32; - let fitness_gain = mutant_fitness - mutation_rates[selected] - interaction_penalty; - - if fitness_gain > best_gain { - best_gain = fitness_gain; - best_swap = Some((u_index, c_index)); - } - } - } - - if let Some((u_index, c_index)) = best_swap { - let added_item = unselected_items[u_index]; - let removed_item = selected_items[c_index]; - - selected_items.swap_remove(c_index); - unselected_items.swap_remove(u_index); - selected_items.push(added_item); - unselected_items.push(removed_item); - - current_value += best_gain; - current_weight = current_weight + challenge.weights[added_item] - challenge.weights[removed_item]; - - if current_weight > challenge.max_weight { - continue; - } - - for index in 0..vertex_count { - mutation_rates[index] += challenge.interaction_values[index][added_item] - - challenge.interaction_values[index][removed_item]; - } - - cooling_schedule[added_item] = 3; - cooling_schedule[removed_item] = 3; - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - - if current_value as u32 > (challenge.min_value * 9 / 10) { - let high_potential_items: Vec = unselected_items - .iter() - .filter(|&&i| challenge.values[i] as i32 > (challenge.min_value as i32 / 4)) - .copied() - .collect(); - - for &item in high_potential_items.iter().take(2) { - if current_weight + challenge.weights[item] <= challenge.max_weight { - selected_items.push(item); - unselected_items.retain(|&x| x != item); - current_weight += challenge.weights[item]; - current_value += challenge.values[item] as i32; - - for &selected in &selected_items { - if selected != item { - current_value += challenge.interaction_values[item][selected]; - } - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - } - } - } - } - - if current_value as u32 >= challenge.min_value && current_weight <= challenge.max_weight { - Ok(Some(Solution { items: selected_items })) - } else { - Ok(None) - } -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - - pub const KERNEL: Option = None; - - 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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/quadkp_improved/mod.rs b/tig-algorithms/src/knapsack/quadkp_improved/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/knapsack/quadkp_improved/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/quadkp_improved/open_data.rs b/tig-algorithms/src/knapsack/quadkp_improved/open_data.rs deleted file mode 100644 index 560b774..0000000 --- a/tig-algorithms/src/knapsack/quadkp_improved/open_data.rs +++ /dev/null @@ -1,188 +0,0 @@ -/*! -Copyright 2024 Rootz - -Licensed under the TIG Open Data 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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use rand::{SeedableRng, Rng, rngs::StdRng}; -use tig_challenges::knapsack::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vertex_count = challenge.weights.len(); - - let mut item_scores: Vec<(usize, f32)> = (0..vertex_count) - .map(|index| { - let interaction_sum: i32 = challenge.interaction_values[index].iter().sum(); - let secondary_score = challenge.values[index] as f32 / challenge.weights[index] as f32; - let combined_score = (challenge.values[index] as f32 * 0.75 + interaction_sum as f32 * 0.15 + secondary_score * 0.1) - / challenge.weights[index] as f32; - (index, combined_score) - }) - .collect(); - - item_scores.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - let mut selected_items = Vec::with_capacity(vertex_count); - let mut unselected_items = Vec::with_capacity(vertex_count); - let mut current_weight = 0; - let mut current_value = 0; - - for &(index, _) in &item_scores { - if current_weight + challenge.weights[index] <= challenge.max_weight { - current_weight += challenge.weights[index]; - current_value += challenge.values[index] as i32; - - for &selected in &selected_items { - current_value += challenge.interaction_values[index][selected]; - } - selected_items.push(index); - } else { - unselected_items.push(index); - } - } - - let mut mutation_rates = vec![0; vertex_count]; - for index in 0..vertex_count { - mutation_rates[index] = challenge.values[index] as i32; - for &selected in &selected_items { - mutation_rates[index] += challenge.interaction_values[index][selected]; - } - } - - let max_generations = 150; - let mut cooling_schedule = vec![0; vertex_count]; - let mut rng = StdRng::seed_from_u64(challenge.seed[0] as u64); - - for generation in 0..max_generations { - let mut best_gain = 0; - let mut best_swap = None; - - for (u_index, &mutant) in unselected_items.iter().enumerate() { - if cooling_schedule[mutant] > 0 { - continue; - } - - let mutant_fitness = mutation_rates[mutant]; - let extra_weight = challenge.weights[mutant] as i32 - (challenge.max_weight as i32 - current_weight as i32); - - if mutant_fitness < 0 { - continue; - } - - for (c_index, &selected) in selected_items.iter().enumerate() { - if cooling_schedule[selected] > 0 { - continue; - } - - if extra_weight > 0 && (challenge.weights[selected] as i32) < extra_weight { - continue; - } - - let interaction_penalty = (challenge.interaction_values[mutant][selected] as f32 * 0.3) as i32; - let fitness_gain = mutant_fitness - mutation_rates[selected] - interaction_penalty; - - if fitness_gain > best_gain { - best_gain = fitness_gain; - best_swap = Some((u_index, c_index)); - } - } - } - - if let Some((u_index, c_index)) = best_swap { - let added_item = unselected_items[u_index]; - let removed_item = selected_items[c_index]; - - selected_items.swap_remove(c_index); - unselected_items.swap_remove(u_index); - selected_items.push(added_item); - unselected_items.push(removed_item); - - current_value += best_gain; - current_weight = current_weight + challenge.weights[added_item] - challenge.weights[removed_item]; - - if current_weight > challenge.max_weight { - continue; - } - - for index in 0..vertex_count { - mutation_rates[index] += challenge.interaction_values[index][added_item] - - challenge.interaction_values[index][removed_item]; - } - - cooling_schedule[added_item] = 3; - cooling_schedule[removed_item] = 3; - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - - for cooling_rate in cooling_schedule.iter_mut() { - *cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 }; - } - - if current_value as u32 > (challenge.min_value * 9 / 10) { - let high_potential_items: Vec = unselected_items - .iter() - .filter(|&&i| challenge.values[i] as i32 > (challenge.min_value as i32 / 4)) - .copied() - .collect(); - - for &item in high_potential_items.iter().take(2) { - if current_weight + challenge.weights[item] <= challenge.max_weight { - selected_items.push(item); - unselected_items.retain(|&x| x != item); - current_weight += challenge.weights[item]; - current_value += challenge.values[item] as i32; - - for &selected in &selected_items { - if selected != item { - current_value += challenge.interaction_values[item][selected]; - } - } - - if current_value as u32 >= challenge.min_value { - return Ok(Some(Solution { items: selected_items })); - } - } - } - } - } - - if current_value as u32 >= challenge.min_value && current_weight <= challenge.max_weight { - Ok(Some(Solution { items: selected_items })) - } else { - Ok(None) - } -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - - pub const KERNEL: Option = None; - - 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}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/template.rs b/tig-algorithms/src/knapsack/template.rs index dab7384..6386cbf 100644 --- a/tig-algorithms/src/knapsack/template.rs +++ b/tig-algorithms/src/knapsack/template.rs @@ -1,7 +1,11 @@ /*! -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.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 @@ -13,6 +17,24 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the speci language governing permissions and limitations under the License. */ +// REMOVE BELOW SECTION IF UNUSED +/* +REFERENCES AND ACKNOWLEDGMENTS + +This implementation is based on or inspired by existing work. Citations and +acknowledgments below: + +1. Academic Papers: + - [Author(s), "Paper Title", DOI (if available)] + +2. Code References: + - [Author(s), URL] + +3. Other: + - [Author(s), Details] + +*/ + // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge use anyhow::{anyhow, Result}; use tig_challenges::knapsack::{Challenge, Solution}; diff --git a/tig-algorithms/src/satisfiability/fast_walk_sat/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/benchmarker_outbound.rs deleted file mode 100644 index dceb0e8..0000000 --- a/tig-algorithms/src/satisfiability/fast_walk_sat/benchmarker_outbound.rs +++ /dev/null @@ -1,250 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 std::collections::HashSet; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual = HashSet::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual.insert(i); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if let Some(&i) = residual.iter().next() { - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - for &c in &n_clauses[v] { - if variables[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } else { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } - } - for &c in &p_clauses[v] { - if variables[v] { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } else { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/fast_walk_sat/commercial.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/commercial.rs deleted file mode 100644 index a71cfb8..0000000 --- a/tig-algorithms/src/satisfiability/fast_walk_sat/commercial.rs +++ /dev/null @@ -1,250 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Commercial License v1.0 (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 std::collections::HashSet; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual = HashSet::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual.insert(i); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if let Some(&i) = residual.iter().next() { - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - for &c in &n_clauses[v] { - if variables[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } else { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } - } - for &c in &p_clauses[v] { - if variables[v] { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } else { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/fast_walk_sat/inbound.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/inbound.rs deleted file mode 100644 index f8bed23..0000000 --- a/tig-algorithms/src/satisfiability/fast_walk_sat/inbound.rs +++ /dev/null @@ -1,250 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -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 std::collections::HashSet; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual = HashSet::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual.insert(i); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if let Some(&i) = residual.iter().next() { - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - for &c in &n_clauses[v] { - if variables[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } else { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } - } - for &c in &p_clauses[v] { - if variables[v] { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } else { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/fast_walk_sat/innovator_outbound.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/innovator_outbound.rs deleted file mode 100644 index 560c2eb..0000000 --- a/tig-algorithms/src/satisfiability/fast_walk_sat/innovator_outbound.rs +++ /dev/null @@ -1,250 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 std::collections::HashSet; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual = HashSet::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual.insert(i); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if let Some(&i) = residual.iter().next() { - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - for &c in &n_clauses[v] { - if variables[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } else { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } - } - for &c in &p_clauses[v] { - if variables[v] { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } else { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/fast_walk_sat/mod.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/fast_walk_sat/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/fast_walk_sat/open_data.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/open_data.rs deleted file mode 100644 index c68797e..0000000 --- a/tig-algorithms/src/satisfiability/fast_walk_sat/open_data.rs +++ /dev/null @@ -1,250 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Open Data 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 std::collections::HashSet; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual = HashSet::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual.insert(i); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if let Some(&i) = residual.iter().next() { - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - for &c in &n_clauses[v] { - if variables[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } else { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } - } - for &c in &p_clauses[v] { - if variables[v] { - if num_good_so_far[c] == 1 { - residual.insert(c); - } - num_good_so_far[c] -= 1; - } else { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - residual.remove(&c); - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/inbound/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/inbound/benchmarker_outbound.rs deleted file mode 100644 index b08ce0d..0000000 --- a/tig-algorithms/src/satisfiability/inbound/benchmarker_outbound.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Clifford Algueraz - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/inbound/commercial.rs b/tig-algorithms/src/satisfiability/inbound/commercial.rs deleted file mode 100644 index b8c9432..0000000 --- a/tig-algorithms/src/satisfiability/inbound/commercial.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Clifford Algueraz - -Licensed under the TIG Commercial License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/inbound/inbound.rs b/tig-algorithms/src/satisfiability/inbound/inbound.rs deleted file mode 100644 index 72ae4d8..0000000 --- a/tig-algorithms/src/satisfiability/inbound/inbound.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Clifford Algueraz - -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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/inbound/innovator_outbound.rs b/tig-algorithms/src/satisfiability/inbound/innovator_outbound.rs deleted file mode 100644 index ca5c0a3..0000000 --- a/tig-algorithms/src/satisfiability/inbound/innovator_outbound.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Clifford Algueraz - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/inbound/mod.rs b/tig-algorithms/src/satisfiability/inbound/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/inbound/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/inbound/open_data.rs b/tig-algorithms/src/satisfiability/inbound/open_data.rs deleted file mode 100644 index 75d7647..0000000 --- a/tig-algorithms/src/satisfiability/inbound/open_data.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Clifford Algueraz - -Licensed under the TIG Open Data 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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/mod.rs b/tig-algorithms/src/satisfiability/mod.rs index b62443c..22d6805 100644 --- a/tig-algorithms/src/satisfiability/mod.rs +++ b/tig-algorithms/src/satisfiability/mod.rs @@ -1,5 +1,4 @@ -pub mod schnoing; -pub use schnoing as c001_a001; +// c001_a001 // c001_a002 @@ -7,8 +6,7 @@ pub use schnoing as c001_a001; // c001_a004 -pub mod walk_sat; -pub use walk_sat as c001_a005; +// c001_a005 // c001_a006 @@ -20,11 +18,9 @@ pub use walk_sat as c001_a005; // c001_a010 -pub mod fast_walk_sat; -pub use fast_walk_sat as c001_a011; +// c001_a011 -pub mod sprint_sat; -pub use sprint_sat as c001_a012; +// c001_a012 // c001_a013 @@ -36,8 +32,7 @@ pub use sprint_sat as c001_a012; // c001_a017 -pub mod inbound; -pub use inbound as c001_a018; +// c001_a018 // c001_a019 @@ -47,8 +42,7 @@ pub use inbound as c001_a018; // c001_a022 -pub mod sat_allocd; -pub use sat_allocd as c001_a023; +// c001_a023 // c001_a024 @@ -64,15 +58,13 @@ pub use sat_allocd as c001_a023; // c001_a030 -pub mod sat_optima; -pub use sat_optima as c001_a031; +// c001_a031 // c001_a032 // c001_a033 -pub mod sat_global; -pub use sat_global as c001_a034; +// c001_a034 // c001_a035 @@ -86,11 +78,9 @@ pub use sat_global as c001_a034; // c001_a040 -pub mod sat_global_opt; -pub use sat_global_opt as c001_a041; +// c001_a041 -pub mod sat_adaptive; -pub use sat_adaptive as c001_a042; +// c001_a042 // c001_a043 diff --git a/tig-algorithms/src/satisfiability/sat_adaptive/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sat_adaptive/benchmarker_outbound.rs deleted file mode 100644 index f3dc67c..0000000 --- a/tig-algorithms/src/satisfiability/sat_adaptive/benchmarker_outbound.rs +++ /dev/null @@ -1,315 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let base_prob = 0.52; - let mut current_prob = base_prob; - let check_interval = 50; - let mut last_check_residual = residual_.len(); - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - if rounds % check_interval == 0 { - let progress = last_check_residual as i64 - residual_.len() as i64; - let progress_ratio = progress as f64 / last_check_residual as f64; - - let progress_threshold = 0.2 + 0.1 * f64::min(1.0, (clauses_ratio - 410.0) / 15.0); - - if progress <= 0 { - let prob_adjustment = 0.025 * (-progress as f64 / last_check_residual as f64).min(1.0); - current_prob = (current_prob + prob_adjustment).min(0.9); - } else if progress_ratio > progress_threshold { - current_prob = base_prob; - } else { - current_prob = current_prob * 0.8 + base_prob * 0.2; - } - - last_check_residual = residual_.len(); - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(current_prob) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_adaptive/commercial.rs b/tig-algorithms/src/satisfiability/sat_adaptive/commercial.rs deleted file mode 100644 index 324e53f..0000000 --- a/tig-algorithms/src/satisfiability/sat_adaptive/commercial.rs +++ /dev/null @@ -1,315 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let base_prob = 0.52; - let mut current_prob = base_prob; - let check_interval = 50; - let mut last_check_residual = residual_.len(); - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - if rounds % check_interval == 0 { - let progress = last_check_residual as i64 - residual_.len() as i64; - let progress_ratio = progress as f64 / last_check_residual as f64; - - let progress_threshold = 0.2 + 0.1 * f64::min(1.0, (clauses_ratio - 410.0) / 15.0); - - if progress <= 0 { - let prob_adjustment = 0.025 * (-progress as f64 / last_check_residual as f64).min(1.0); - current_prob = (current_prob + prob_adjustment).min(0.9); - } else if progress_ratio > progress_threshold { - current_prob = base_prob; - } else { - current_prob = current_prob * 0.8 + base_prob * 0.2; - } - - last_check_residual = residual_.len(); - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(current_prob) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_adaptive/inbound.rs b/tig-algorithms/src/satisfiability/sat_adaptive/inbound.rs deleted file mode 100644 index 885501f..0000000 --- a/tig-algorithms/src/satisfiability/sat_adaptive/inbound.rs +++ /dev/null @@ -1,315 +0,0 @@ -/*! -Copyright 2024 syebastian - -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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let base_prob = 0.52; - let mut current_prob = base_prob; - let check_interval = 50; - let mut last_check_residual = residual_.len(); - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - if rounds % check_interval == 0 { - let progress = last_check_residual as i64 - residual_.len() as i64; - let progress_ratio = progress as f64 / last_check_residual as f64; - - let progress_threshold = 0.2 + 0.1 * f64::min(1.0, (clauses_ratio - 410.0) / 15.0); - - if progress <= 0 { - let prob_adjustment = 0.025 * (-progress as f64 / last_check_residual as f64).min(1.0); - current_prob = (current_prob + prob_adjustment).min(0.9); - } else if progress_ratio > progress_threshold { - current_prob = base_prob; - } else { - current_prob = current_prob * 0.8 + base_prob * 0.2; - } - - last_check_residual = residual_.len(); - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(current_prob) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_adaptive/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sat_adaptive/innovator_outbound.rs deleted file mode 100644 index 38b12c6..0000000 --- a/tig-algorithms/src/satisfiability/sat_adaptive/innovator_outbound.rs +++ /dev/null @@ -1,315 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let base_prob = 0.52; - let mut current_prob = base_prob; - let check_interval = 50; - let mut last_check_residual = residual_.len(); - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - if rounds % check_interval == 0 { - let progress = last_check_residual as i64 - residual_.len() as i64; - let progress_ratio = progress as f64 / last_check_residual as f64; - - let progress_threshold = 0.2 + 0.1 * f64::min(1.0, (clauses_ratio - 410.0) / 15.0); - - if progress <= 0 { - let prob_adjustment = 0.025 * (-progress as f64 / last_check_residual as f64).min(1.0); - current_prob = (current_prob + prob_adjustment).min(0.9); - } else if progress_ratio > progress_threshold { - current_prob = base_prob; - } else { - current_prob = current_prob * 0.8 + base_prob * 0.2; - } - - last_check_residual = residual_.len(); - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(current_prob) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_adaptive/mod.rs b/tig-algorithms/src/satisfiability/sat_adaptive/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/sat_adaptive/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sat_adaptive/open_data.rs b/tig-algorithms/src/satisfiability/sat_adaptive/open_data.rs deleted file mode 100644 index 4a4711b..0000000 --- a/tig-algorithms/src/satisfiability/sat_adaptive/open_data.rs +++ /dev/null @@ -1,315 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let base_prob = 0.52; - let mut current_prob = base_prob; - let check_interval = 50; - let mut last_check_residual = residual_.len(); - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - if rounds % check_interval == 0 { - let progress = last_check_residual as i64 - residual_.len() as i64; - let progress_ratio = progress as f64 / last_check_residual as f64; - - let progress_threshold = 0.2 + 0.1 * f64::min(1.0, (clauses_ratio - 410.0) / 15.0); - - if progress <= 0 { - let prob_adjustment = 0.025 * (-progress as f64 / last_check_residual as f64).min(1.0); - current_prob = (current_prob + prob_adjustment).min(0.9); - } else if progress_ratio > progress_threshold { - current_prob = base_prob; - } else { - current_prob = current_prob * 0.8 + base_prob * 0.2; - } - - last_check_residual = residual_.len(); - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(current_prob) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_allocd/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sat_allocd/benchmarker_outbound.rs deleted file mode 100644 index 2e6758f..0000000 --- a/tig-algorithms/src/satisfiability/sat_allocd/benchmarker_outbound.rs +++ /dev/null @@ -1,284 +0,0 @@ -/*! -Copyright 2024 AllFather - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_allocd/commercial.rs b/tig-algorithms/src/satisfiability/sat_allocd/commercial.rs deleted file mode 100644 index 53644aa..0000000 --- a/tig-algorithms/src/satisfiability/sat_allocd/commercial.rs +++ /dev/null @@ -1,284 +0,0 @@ -/*! -Copyright 2024 AllFather - -Licensed under the TIG Commercial License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_allocd/inbound.rs b/tig-algorithms/src/satisfiability/sat_allocd/inbound.rs deleted file mode 100644 index 8cfe0c9..0000000 --- a/tig-algorithms/src/satisfiability/sat_allocd/inbound.rs +++ /dev/null @@ -1,284 +0,0 @@ -/*! -Copyright 2024 AllFather - -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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_allocd/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sat_allocd/innovator_outbound.rs deleted file mode 100644 index 6452120..0000000 --- a/tig-algorithms/src/satisfiability/sat_allocd/innovator_outbound.rs +++ /dev/null @@ -1,284 +0,0 @@ -/*! -Copyright 2024 AllFather - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_allocd/mod.rs b/tig-algorithms/src/satisfiability/sat_allocd/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/sat_allocd/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sat_allocd/open_data.rs b/tig-algorithms/src/satisfiability/sat_allocd/open_data.rs deleted file mode 100644 index 5dc5c06..0000000 --- a/tig-algorithms/src/satisfiability/sat_allocd/open_data.rs +++ /dev/null @@ -1,284 +0,0 @@ -/*! -Copyright 2024 AllFather - -Licensed under the TIG Open Data 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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_global/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sat_global/benchmarker_outbound.rs deleted file mode 100644 index 03d219f..0000000 --- a/tig-algorithms/src/satisfiability/sat_global/benchmarker_outbound.rs +++ /dev/null @@ -1,297 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - - - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let vad = num_p as f32 / (num_p + num_n).max(1) as f32; - - if vad >= 1.8 { - variables[v] = true; - } else if vad <= 0.56 { - variables[v] = false; - } else { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 900.0 + 1.8 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global/commercial.rs b/tig-algorithms/src/satisfiability/sat_global/commercial.rs deleted file mode 100644 index d026725..0000000 --- a/tig-algorithms/src/satisfiability/sat_global/commercial.rs +++ /dev/null @@ -1,297 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - - - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let vad = num_p as f32 / (num_p + num_n).max(1) as f32; - - if vad >= 1.8 { - variables[v] = true; - } else if vad <= 0.56 { - variables[v] = false; - } else { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 900.0 + 1.8 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global/inbound.rs b/tig-algorithms/src/satisfiability/sat_global/inbound.rs deleted file mode 100644 index fd50847..0000000 --- a/tig-algorithms/src/satisfiability/sat_global/inbound.rs +++ /dev/null @@ -1,297 +0,0 @@ -/*! -Copyright 2024 syebastian - -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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - - - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let vad = num_p as f32 / (num_p + num_n).max(1) as f32; - - if vad >= 1.8 { - variables[v] = true; - } else if vad <= 0.56 { - variables[v] = false; - } else { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 900.0 + 1.8 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sat_global/innovator_outbound.rs deleted file mode 100644 index 80b6edc..0000000 --- a/tig-algorithms/src/satisfiability/sat_global/innovator_outbound.rs +++ /dev/null @@ -1,297 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - - - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let vad = num_p as f32 / (num_p + num_n).max(1) as f32; - - if vad >= 1.8 { - variables[v] = true; - } else if vad <= 0.56 { - variables[v] = false; - } else { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 900.0 + 1.8 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global/mod.rs b/tig-algorithms/src/satisfiability/sat_global/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/sat_global/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sat_global/open_data.rs b/tig-algorithms/src/satisfiability/sat_global/open_data.rs deleted file mode 100644 index f1cbf28..0000000 --- a/tig-algorithms/src/satisfiability/sat_global/open_data.rs +++ /dev/null @@ -1,297 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - - - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let vad = num_p as f32 / (num_p + num_n).max(1) as f32; - - if vad >= 1.8 { - variables[v] = true; - } else if vad <= 0.56 { - variables[v] = false; - } else { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 900.0 + 1.8 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global_opt/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sat_global_opt/benchmarker_outbound.rs deleted file mode 100644 index bf1e166..0000000 --- a/tig-algorithms/src/satisfiability/sat_global_opt/benchmarker_outbound.rs +++ /dev/null @@ -1,292 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global_opt/commercial.rs b/tig-algorithms/src/satisfiability/sat_global_opt/commercial.rs deleted file mode 100644 index 1a41da5..0000000 --- a/tig-algorithms/src/satisfiability/sat_global_opt/commercial.rs +++ /dev/null @@ -1,292 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global_opt/inbound.rs b/tig-algorithms/src/satisfiability/sat_global_opt/inbound.rs deleted file mode 100644 index b39f496..0000000 --- a/tig-algorithms/src/satisfiability/sat_global_opt/inbound.rs +++ /dev/null @@ -1,292 +0,0 @@ -/*! -Copyright 2024 syebastian - -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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global_opt/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sat_global_opt/innovator_outbound.rs deleted file mode 100644 index 18bbd68..0000000 --- a/tig-algorithms/src/satisfiability/sat_global_opt/innovator_outbound.rs +++ /dev/null @@ -1,292 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_global_opt/mod.rs b/tig-algorithms/src/satisfiability/sat_global_opt/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/sat_global_opt/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sat_global_opt/open_data.rs b/tig-algorithms/src/satisfiability/sat_global_opt/open_data.rs deleted file mode 100644 index eb4a1c0..0000000 --- a/tig-algorithms/src/satisfiability/sat_global_opt/open_data.rs +++ /dev/null @@ -1,292 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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::{SmallRng, StdRng}, Rng, SeedableRng}; -use std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = SmallRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - } else { - n_clauses[var].push(i); - } - } - } - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - let num_p = p_clauses[v].len(); - let num_n = n_clauses[v].len(); - - let nad = 1.28; - let mut vad = nad + 1.0; - if num_n > 0 { - vad = num_p as f32 / num_n as f32; - } - - if vad <= nad { - variables[v] = false; - } else { - let prob = num_p as f64 / (num_p + num_n).max(1) as f64; - variables[v] = rng.gen_bool(prob) - } - } - - let mut num_good_so_far: Vec = vec![0; num_clauses]; - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 && variables[var] { - num_good_so_far[i] += 1 - } else if l < 0 && !variables[var] { - num_good_so_far[i] += 1 - } - } - } - - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = vec![None; num_clauses]; - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices[i] = Some(residual_.len() - 1); - } - } - - let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; - let num_vars = challenge.difficulty.num_variables as f64; - let max_fuel = 2000000000.0; - let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; - let flip_fuel = 350.0 + 0.9 * clauses_ratio; - let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; - loop { - if !residual_.is_empty() { - - let rand_val = rng.gen::(); - - let i = residual_[rand_val % residual_.len()]; - let mut min_sad = clauses.len(); - let mut v_min_sad = usize::MAX; - let c = &mut clauses[i]; - - if c.len() > 1 { - let random_index = rand_val % c.len(); - c.swap(0, random_index); - } - for &l in c.iter() { - let abs_l = l.abs() as usize - 1; - let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; - - let mut sad = 0; - for &c in clauses_to_check { - if num_good_so_far[c] == 1 { - sad += 1; - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = abs_l; - } - } - - let v = if min_sad == 0 { - v_min_sad - } else if rng.gen_bool(0.5) { - c[0].abs() as usize - 1 - } else { - v_min_sad - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices[c] = Some(residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices[c].take().unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices[last] = Some(i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= max_num_rounds { - return Ok(None); - } - } - return 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 = 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/sat_optima/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sat_optima/benchmarker_outbound.rs deleted file mode 100644 index 6a20e5b..0000000 --- a/tig-algorithms/src/satisfiability/sat_optima/benchmarker_outbound.rs +++ /dev/null @@ -1,285 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - - let i = residual_[rng.gen_range(0..residual_.len())]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_optima/commercial.rs b/tig-algorithms/src/satisfiability/sat_optima/commercial.rs deleted file mode 100644 index 1c4e87a..0000000 --- a/tig-algorithms/src/satisfiability/sat_optima/commercial.rs +++ /dev/null @@ -1,285 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - - let i = residual_[rng.gen_range(0..residual_.len())]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_optima/inbound.rs b/tig-algorithms/src/satisfiability/sat_optima/inbound.rs deleted file mode 100644 index 4d56d06..0000000 --- a/tig-algorithms/src/satisfiability/sat_optima/inbound.rs +++ /dev/null @@ -1,285 +0,0 @@ -/*! -Copyright 2024 syebastian - -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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - - let i = residual_[rng.gen_range(0..residual_.len())]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_optima/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sat_optima/innovator_outbound.rs deleted file mode 100644 index d718930..0000000 --- a/tig-algorithms/src/satisfiability/sat_optima/innovator_outbound.rs +++ /dev/null @@ -1,285 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - - let i = residual_[rng.gen_range(0..residual_.len())]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/sat_optima/mod.rs b/tig-algorithms/src/satisfiability/sat_optima/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/sat_optima/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sat_optima/open_data.rs b/tig-algorithms/src/satisfiability/sat_optima/open_data.rs deleted file mode 100644 index 19eda8b..0000000 --- a/tig-algorithms/src/satisfiability/sat_optima/open_data.rs +++ /dev/null @@ -1,285 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut rounds = 0; - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; - let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - // Preallocate capacity for p_clauses and n_clauses - for c in &clauses { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - if p_clauses[var].capacity() == 0 { - p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } else { - if n_clauses[var].capacity() == 0 { - n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); - } - } - } - } - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - loop { - if !residual_.is_empty() { - - let i = residual_[rng.gen_range(0..residual_.len())]; - let mut min_sad = clauses.len(); - let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad.clear(); - v_min_sad.push((l.abs() - 1) as usize); - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - rounds += 1; - if rounds >= num_variables * 35 { - return Ok(None); - } - } - - return 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 = 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/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/schnoing/benchmarker_outbound.rs deleted file mode 100644 index 37de9a2..0000000 --- a/tig-algorithms/src/satisfiability/schnoing/benchmarker_outbound.rs +++ /dev/null @@ -1,87 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) 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 = 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/commercial.rs b/tig-algorithms/src/satisfiability/schnoing/commercial.rs deleted file mode 100644 index fccc582..0000000 --- a/tig-algorithms/src/satisfiability/schnoing/commercial.rs +++ /dev/null @@ -1,87 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Commercial License v1.0 (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(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) 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 = 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/cuda_example.rs b/tig-algorithms/src/satisfiability/schnoing/cuda_example.rs deleted file mode 100644 index 908b9ed..0000000 --- a/tig-algorithms/src/satisfiability/schnoing/cuda_example.rs +++ /dev/null @@ -1,156 +0,0 @@ -/*! -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/inbound.rs b/tig-algorithms/src/satisfiability/schnoing/inbound.rs deleted file mode 100644 index 60efa83..0000000 --- a/tig-algorithms/src/satisfiability/schnoing/inbound.rs +++ /dev/null @@ -1,87 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -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(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) 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 = 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/innovator_outbound.rs b/tig-algorithms/src/satisfiability/schnoing/innovator_outbound.rs deleted file mode 100644 index d925304..0000000 --- a/tig-algorithms/src/satisfiability/schnoing/innovator_outbound.rs +++ /dev/null @@ -1,87 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Innovator Outbound Game License v1.0 (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(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) 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 = 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 deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/schnoing/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/schnoing/open_data.rs b/tig-algorithms/src/satisfiability/schnoing/open_data.rs deleted file mode 100644 index 93bef13..0000000 --- a/tig-algorithms/src/satisfiability/schnoing/open_data.rs +++ /dev/null @@ -1,87 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Open Data 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(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) 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 = 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/sprint_sat/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sprint_sat/benchmarker_outbound.rs deleted file mode 100644 index 648e969..0000000 --- a/tig-algorithms/src/satisfiability/sprint_sat/benchmarker_outbound.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/sprint_sat/commercial.rs b/tig-algorithms/src/satisfiability/sprint_sat/commercial.rs deleted file mode 100644 index 24e2b1a..0000000 --- a/tig-algorithms/src/satisfiability/sprint_sat/commercial.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Commercial License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/sprint_sat/inbound.rs b/tig-algorithms/src/satisfiability/sprint_sat/inbound.rs deleted file mode 100644 index 18ade15..0000000 --- a/tig-algorithms/src/satisfiability/sprint_sat/inbound.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/sprint_sat/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sprint_sat/innovator_outbound.rs deleted file mode 100644 index e71c8bb..0000000 --- a/tig-algorithms/src/satisfiability/sprint_sat/innovator_outbound.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/sprint_sat/mod.rs b/tig-algorithms/src/satisfiability/sprint_sat/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/sprint_sat/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sprint_sat/open_data.rs b/tig-algorithms/src/satisfiability/sprint_sat/open_data.rs deleted file mode 100644 index 61e8321..0000000 --- a/tig-algorithms/src/satisfiability/sprint_sat/open_data.rs +++ /dev/null @@ -1,267 +0,0 @@ -/*! -Copyright 2024 Dominic Kennedy - -Licensed under the TIG Open Data 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 std::collections::HashMap; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - - let mut p_single = vec![false; challenge.difficulty.num_variables]; - let mut n_single = vec![false; challenge.difficulty.num_variables]; - - let mut clauses_ = challenge.clauses.clone(); - let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); - - let mut dead = false; - - while !(dead) { - let mut done = true; - for c in &clauses_ { - let mut c_: Vec = Vec::with_capacity(c.len()); - let mut skip = false; - for (i, l) in c.iter().enumerate() { - if (p_single[(l.abs() - 1) as usize] && *l > 0) - || (n_single[(l.abs() - 1) as usize] && *l < 0) - || c[(i + 1)..].contains(&-l) - { - skip = true; - break; - } else if p_single[(l.abs() - 1) as usize] - || n_single[(l.abs() - 1) as usize] - || c[(i + 1)..].contains(&l) - { - done = false; - continue; - } else { - c_.push(*l); - } - } - if skip { - done = false; - continue; - }; - match c_[..] { - [l] => { - done = false; - if l > 0 { - if n_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - p_single[(l.abs() - 1) as usize] = true; - } - } else { - if p_single[(l.abs() - 1) as usize] { - dead = true; - break; - } else { - n_single[(l.abs() - 1) as usize] = true; - } - } - } - [] => { - dead = true; - break; - } - _ => { - clauses.push(c_); - } - } - } - if done { - break; - } else { - clauses_ = clauses; - clauses = Vec::with_capacity(clauses_.len()); - } - } - - if dead { - return Ok(None); - } - - let num_variables = challenge.difficulty.num_variables; - let num_clauses = clauses.len(); - - let mut p_clauses: Vec> = vec![vec![]; num_variables]; - let mut n_clauses: Vec> = vec![vec![]; num_variables]; - - let mut variables = vec![false; num_variables]; - for v in 0..num_variables { - if p_single[v] { - variables[v] = true - } else if n_single[v] { - variables[v] = false - } else { - variables[v] = rng.gen_bool(0.5) - } - } - let mut num_good_so_far: Vec = vec![0; num_clauses]; - - for (i, &ref c) in clauses.iter().enumerate() { - for &l in c { - let var = (l.abs() - 1) as usize; - if l > 0 { - p_clauses[var].push(i); - if variables[var] { - num_good_so_far[i] += 1 - } - } else { - n_clauses[var].push(i); - if !variables[var] { - num_good_so_far[i] += 1 - } - } - } - } - - let mut residual_ = Vec::with_capacity(num_clauses); - let mut residual_indices = HashMap::with_capacity(num_clauses); - - for (i, &num_good) in num_good_so_far.iter().enumerate() { - if num_good == 0 { - residual_.push(i); - residual_indices.insert(i, residual_.len() - 1); - } - } - - let mut attempts = 0; - loop { - if attempts >= num_variables * 25 { - return Ok(None); - } - if !residual_.is_empty() { - let i = residual_[0]; - let mut min_sad = clauses.len(); - let mut v_min_sad = vec![]; - let c = &clauses[i]; - for &l in c { - let mut sad = 0 as usize; - if variables[(l.abs() - 1) as usize] { - for &c in &p_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } else { - for &c in &n_clauses[(l.abs() - 1) as usize] { - if num_good_so_far[c] == 1 { - sad += 1; - if sad > min_sad { - break; - } - } - } - } - - if sad < min_sad { - min_sad = sad; - v_min_sad = vec![(l.abs() - 1) as usize]; - } else if sad == min_sad { - v_min_sad.push((l.abs() - 1) as usize); - } - } - let v = if min_sad == 0 { - if v_min_sad.len() == 1 { - v_min_sad[0] - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - } else { - if rng.gen_bool(0.5) { - let l = c[rng.gen_range(0..c.len())]; - (l.abs() - 1) as usize - } else { - v_min_sad[rng.gen_range(0..v_min_sad.len())] - } - }; - - if variables[v] { - for &c in &n_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - for &c in &p_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - } else { - for &c in &n_clauses[v] { - if num_good_so_far[c] == 1 { - residual_.push(c); - residual_indices.insert(c, residual_.len() - 1); - } - num_good_so_far[c] -= 1; - } - - for &c in &p_clauses[v] { - num_good_so_far[c] += 1; - if num_good_so_far[c] == 1 { - let i = residual_indices.remove(&c).unwrap(); - let last = residual_.pop().unwrap(); - if i < residual_.len() { - residual_[i] = last; - residual_indices.insert(last, i); - } - } - } - } - - variables[v] = !variables[v]; - } else { - break; - } - attempts += 1; - } - - return 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 = 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/template.rs b/tig-algorithms/src/satisfiability/template.rs index f11c4cd..c2d7153 100644 --- a/tig-algorithms/src/satisfiability/template.rs +++ b/tig-algorithms/src/satisfiability/template.rs @@ -1,7 +1,11 @@ /*! -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.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 @@ -13,6 +17,24 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the speci language governing permissions and limitations under the License. */ +// REMOVE BELOW SECTION IF UNUSED +/* +REFERENCES AND ACKNOWLEDGMENTS + +This implementation is based on or inspired by existing work. Citations and +acknowledgments below: + +1. Academic Papers: + - [Author(s), "Paper Title", DOI (if available)] + +2. Code References: + - [Author(s), URL] + +3. Other: + - [Author(s), Details] + +*/ + // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge use anyhow::{anyhow, Result}; use tig_challenges::satisfiability::{Challenge, Solution}; diff --git a/tig-algorithms/src/satisfiability/walk_sat/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/walk_sat/benchmarker_outbound.rs deleted file mode 100644 index 68650eb..0000000 --- a/tig-algorithms/src/satisfiability/walk_sat/benchmarker_outbound.rs +++ /dev/null @@ -1,76 +0,0 @@ -/*! -Copyright 2024 Chad Blanchard - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use rand::prelude::*; -use rand::rngs::StdRng; -use rand::SeedableRng; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - let num_variables = challenge.difficulty.num_variables; - let max_flips = 1000; - - let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); - - for _ in 0..max_flips { - let mut unsatisfied_clauses: Vec<&Vec> = challenge - .clauses - .iter() - .filter(|clause| !clause_satisfied(clause, &variables)) - .collect(); - - if unsatisfied_clauses.is_empty() { - return Ok(Some(Solution { variables })); - } - - let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); - let literal = clause.choose(&mut rng).unwrap(); - let var_idx = literal.abs() as usize - 1; - variables[var_idx] = !variables[var_idx]; - } - - Ok(None) -} - -fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { - clause.iter().any(|&literal| { - let var_idx = literal.abs() as usize - 1; - (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) - }) -} - -#[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/walk_sat/commercial.rs b/tig-algorithms/src/satisfiability/walk_sat/commercial.rs deleted file mode 100644 index 9b49cbe..0000000 --- a/tig-algorithms/src/satisfiability/walk_sat/commercial.rs +++ /dev/null @@ -1,76 +0,0 @@ -/*! -Copyright 2024 Chad Blanchard - -Licensed under the TIG Commercial License v1.0 (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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use rand::prelude::*; -use rand::rngs::StdRng; -use rand::SeedableRng; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - let num_variables = challenge.difficulty.num_variables; - let max_flips = 1000; - - let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); - - for _ in 0..max_flips { - let mut unsatisfied_clauses: Vec<&Vec> = challenge - .clauses - .iter() - .filter(|clause| !clause_satisfied(clause, &variables)) - .collect(); - - if unsatisfied_clauses.is_empty() { - return Ok(Some(Solution { variables })); - } - - let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); - let literal = clause.choose(&mut rng).unwrap(); - let var_idx = literal.abs() as usize - 1; - variables[var_idx] = !variables[var_idx]; - } - - Ok(None) -} - -fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { - clause.iter().any(|&literal| { - let var_idx = literal.abs() as usize - 1; - (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) - }) -} - -#[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/walk_sat/inbound.rs b/tig-algorithms/src/satisfiability/walk_sat/inbound.rs deleted file mode 100644 index 9f6ebfe..0000000 --- a/tig-algorithms/src/satisfiability/walk_sat/inbound.rs +++ /dev/null @@ -1,76 +0,0 @@ -/*! -Copyright 2024 Chad Blanchard - -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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use rand::prelude::*; -use rand::rngs::StdRng; -use rand::SeedableRng; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - let num_variables = challenge.difficulty.num_variables; - let max_flips = 1000; - - let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); - - for _ in 0..max_flips { - let mut unsatisfied_clauses: Vec<&Vec> = challenge - .clauses - .iter() - .filter(|clause| !clause_satisfied(clause, &variables)) - .collect(); - - if unsatisfied_clauses.is_empty() { - return Ok(Some(Solution { variables })); - } - - let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); - let literal = clause.choose(&mut rng).unwrap(); - let var_idx = literal.abs() as usize - 1; - variables[var_idx] = !variables[var_idx]; - } - - Ok(None) -} - -fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { - clause.iter().any(|&literal| { - let var_idx = literal.abs() as usize - 1; - (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) - }) -} - -#[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/walk_sat/innovator_outbound.rs b/tig-algorithms/src/satisfiability/walk_sat/innovator_outbound.rs deleted file mode 100644 index 2eba628..0000000 --- a/tig-algorithms/src/satisfiability/walk_sat/innovator_outbound.rs +++ /dev/null @@ -1,76 +0,0 @@ -/*! -Copyright 2024 Chad Blanchard - -Licensed under the TIG Innovator Outbound Game License v1.0 (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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use rand::prelude::*; -use rand::rngs::StdRng; -use rand::SeedableRng; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - let num_variables = challenge.difficulty.num_variables; - let max_flips = 1000; - - let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); - - for _ in 0..max_flips { - let mut unsatisfied_clauses: Vec<&Vec> = challenge - .clauses - .iter() - .filter(|clause| !clause_satisfied(clause, &variables)) - .collect(); - - if unsatisfied_clauses.is_empty() { - return Ok(Some(Solution { variables })); - } - - let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); - let literal = clause.choose(&mut rng).unwrap(); - let var_idx = literal.abs() as usize - 1; - variables[var_idx] = !variables[var_idx]; - } - - Ok(None) -} - -fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { - clause.iter().any(|&literal| { - let var_idx = literal.abs() as usize - 1; - (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) - }) -} - -#[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/walk_sat/mod.rs b/tig-algorithms/src/satisfiability/walk_sat/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/satisfiability/walk_sat/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/walk_sat/open_data.rs b/tig-algorithms/src/satisfiability/walk_sat/open_data.rs deleted file mode 100644 index 4bdcf81..0000000 --- a/tig-algorithms/src/satisfiability/walk_sat/open_data.rs +++ /dev/null @@ -1,76 +0,0 @@ -/*! -Copyright 2024 Chad Blanchard - -Licensed under the TIG Open Data 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. -*/ - -// TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use rand::prelude::*; -use rand::rngs::StdRng; -use rand::SeedableRng; -use tig_challenges::satisfiability::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); - let num_variables = challenge.difficulty.num_variables; - let max_flips = 1000; - - let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); - - for _ in 0..max_flips { - let mut unsatisfied_clauses: Vec<&Vec> = challenge - .clauses - .iter() - .filter(|clause| !clause_satisfied(clause, &variables)) - .collect(); - - if unsatisfied_clauses.is_empty() { - return Ok(Some(Solution { variables })); - } - - let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); - let literal = clause.choose(&mut rng).unwrap(); - let var_idx = literal.abs() as usize - 1; - variables[var_idx] = !variables[var_idx]; - } - - Ok(None) -} - -fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { - clause.iter().any(|&literal| { - let var_idx = literal.abs() as usize - 1; - (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) - }) -} - -#[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/vector_search/brute_force_bacalhau/benchmarker_outbound.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/benchmarker_outbound.rs deleted file mode 100644 index 8a58f5c..0000000 --- a/tig-algorithms/src/vector_search/brute_force_bacalhau/benchmarker_outbound.rs +++ /dev/null @@ -1,113 +0,0 @@ -/*! -Copyright 2024 Louis Silva - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 anyhow::Result; - -use tig_challenges::vector_search::*; - -#[inline] -fn l2_norm(x: &[f32]) -> f32 { - x.iter().map(|&val| val * val).sum::().sqrt() -} - -#[inline] -fn euclidean_distance_with_precomputed_norm( - a_norm_sq: f32, - b_norm_sq: f32, - ab_dot_product: f32 -) -> f32 { - (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() -} - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vector_database: &Vec> = &challenge.vector_database; - let query_vectors: &Vec> = &challenge.query_vectors; - let max_distance: f32 = challenge.max_distance; - - let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); - let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); - - let mut sum_norms_sq: f32 = 0.0; - let mut sum_squares: f32 = 0.0; - - for vector in vector_database { - let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); - sum_norms_sq += norm_sq.sqrt(); - sum_squares += norm_sq; - vector_norms_sq.push(norm_sq); - } - - let vector_norms_len: f32 = vector_norms_sq.len() as f32; - let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); - let norm_threshold: f32 = 2.0 * std_dev; - - for query in query_vectors { - let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); - - let mut closest_index: Option = None; - let mut closest_distance: f32 = f32::MAX; - - for (idx, vector) in vector_database.iter().enumerate() { - let vector_norm_sq = vector_norms_sq[idx]; - if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { - continue; - } - - let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); - let distance: f32 = euclidean_distance_with_precomputed_norm( - query_norm_sq, - vector_norm_sq, - ab_dot_product, - ); - - if distance <= max_distance { - closest_index = Some(idx); - break; // Early exit - } else if distance < closest_distance { - closest_index = Some(idx); - closest_distance = distance; - } - } - - if let Some(index) = closest_index { - indexes.push(index); - } else { - return Ok(None); - } - } - - Ok(Some(Solution { indexes })) -} -#[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/vector_search/brute_force_bacalhau/commercial.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/commercial.rs deleted file mode 100644 index bacee69..0000000 --- a/tig-algorithms/src/vector_search/brute_force_bacalhau/commercial.rs +++ /dev/null @@ -1,113 +0,0 @@ -/*! -Copyright 2024 Louis Silva - -Licensed under the TIG Commercial License v1.0 (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 anyhow::Result; - -use tig_challenges::vector_search::*; - -#[inline] -fn l2_norm(x: &[f32]) -> f32 { - x.iter().map(|&val| val * val).sum::().sqrt() -} - -#[inline] -fn euclidean_distance_with_precomputed_norm( - a_norm_sq: f32, - b_norm_sq: f32, - ab_dot_product: f32 -) -> f32 { - (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() -} - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vector_database: &Vec> = &challenge.vector_database; - let query_vectors: &Vec> = &challenge.query_vectors; - let max_distance: f32 = challenge.max_distance; - - let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); - let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); - - let mut sum_norms_sq: f32 = 0.0; - let mut sum_squares: f32 = 0.0; - - for vector in vector_database { - let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); - sum_norms_sq += norm_sq.sqrt(); - sum_squares += norm_sq; - vector_norms_sq.push(norm_sq); - } - - let vector_norms_len: f32 = vector_norms_sq.len() as f32; - let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); - let norm_threshold: f32 = 2.0 * std_dev; - - for query in query_vectors { - let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); - - let mut closest_index: Option = None; - let mut closest_distance: f32 = f32::MAX; - - for (idx, vector) in vector_database.iter().enumerate() { - let vector_norm_sq = vector_norms_sq[idx]; - if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { - continue; - } - - let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); - let distance: f32 = euclidean_distance_with_precomputed_norm( - query_norm_sq, - vector_norm_sq, - ab_dot_product, - ); - - if distance <= max_distance { - closest_index = Some(idx); - break; // Early exit - } else if distance < closest_distance { - closest_index = Some(idx); - closest_distance = distance; - } - } - - if let Some(index) = closest_index { - indexes.push(index); - } else { - return Ok(None); - } - } - - Ok(Some(Solution { indexes })) -} -#[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/vector_search/brute_force_bacalhau/inbound.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/inbound.rs deleted file mode 100644 index fd0bf1d..0000000 --- a/tig-algorithms/src/vector_search/brute_force_bacalhau/inbound.rs +++ /dev/null @@ -1,113 +0,0 @@ -/*! -Copyright 2024 Louis Silva - -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 anyhow::Result; - -use tig_challenges::vector_search::*; - -#[inline] -fn l2_norm(x: &[f32]) -> f32 { - x.iter().map(|&val| val * val).sum::().sqrt() -} - -#[inline] -fn euclidean_distance_with_precomputed_norm( - a_norm_sq: f32, - b_norm_sq: f32, - ab_dot_product: f32 -) -> f32 { - (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() -} - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vector_database: &Vec> = &challenge.vector_database; - let query_vectors: &Vec> = &challenge.query_vectors; - let max_distance: f32 = challenge.max_distance; - - let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); - let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); - - let mut sum_norms_sq: f32 = 0.0; - let mut sum_squares: f32 = 0.0; - - for vector in vector_database { - let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); - sum_norms_sq += norm_sq.sqrt(); - sum_squares += norm_sq; - vector_norms_sq.push(norm_sq); - } - - let vector_norms_len: f32 = vector_norms_sq.len() as f32; - let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); - let norm_threshold: f32 = 2.0 * std_dev; - - for query in query_vectors { - let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); - - let mut closest_index: Option = None; - let mut closest_distance: f32 = f32::MAX; - - for (idx, vector) in vector_database.iter().enumerate() { - let vector_norm_sq = vector_norms_sq[idx]; - if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { - continue; - } - - let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); - let distance: f32 = euclidean_distance_with_precomputed_norm( - query_norm_sq, - vector_norm_sq, - ab_dot_product, - ); - - if distance <= max_distance { - closest_index = Some(idx); - break; // Early exit - } else if distance < closest_distance { - closest_index = Some(idx); - closest_distance = distance; - } - } - - if let Some(index) = closest_index { - indexes.push(index); - } else { - return Ok(None); - } - } - - Ok(Some(Solution { indexes })) -} -#[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/vector_search/brute_force_bacalhau/innovator_outbound.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/innovator_outbound.rs deleted file mode 100644 index a0996b6..0000000 --- a/tig-algorithms/src/vector_search/brute_force_bacalhau/innovator_outbound.rs +++ /dev/null @@ -1,113 +0,0 @@ -/*! -Copyright 2024 Louis Silva - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 anyhow::Result; - -use tig_challenges::vector_search::*; - -#[inline] -fn l2_norm(x: &[f32]) -> f32 { - x.iter().map(|&val| val * val).sum::().sqrt() -} - -#[inline] -fn euclidean_distance_with_precomputed_norm( - a_norm_sq: f32, - b_norm_sq: f32, - ab_dot_product: f32 -) -> f32 { - (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() -} - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vector_database: &Vec> = &challenge.vector_database; - let query_vectors: &Vec> = &challenge.query_vectors; - let max_distance: f32 = challenge.max_distance; - - let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); - let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); - - let mut sum_norms_sq: f32 = 0.0; - let mut sum_squares: f32 = 0.0; - - for vector in vector_database { - let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); - sum_norms_sq += norm_sq.sqrt(); - sum_squares += norm_sq; - vector_norms_sq.push(norm_sq); - } - - let vector_norms_len: f32 = vector_norms_sq.len() as f32; - let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); - let norm_threshold: f32 = 2.0 * std_dev; - - for query in query_vectors { - let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); - - let mut closest_index: Option = None; - let mut closest_distance: f32 = f32::MAX; - - for (idx, vector) in vector_database.iter().enumerate() { - let vector_norm_sq = vector_norms_sq[idx]; - if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { - continue; - } - - let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); - let distance: f32 = euclidean_distance_with_precomputed_norm( - query_norm_sq, - vector_norm_sq, - ab_dot_product, - ); - - if distance <= max_distance { - closest_index = Some(idx); - break; // Early exit - } else if distance < closest_distance { - closest_index = Some(idx); - closest_distance = distance; - } - } - - if let Some(index) = closest_index { - indexes.push(index); - } else { - return Ok(None); - } - } - - Ok(Some(Solution { indexes })) -} -#[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/vector_search/brute_force_bacalhau/mod.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vector_search/brute_force_bacalhau/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vector_search/brute_force_bacalhau/open_data.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/open_data.rs deleted file mode 100644 index c55800e..0000000 --- a/tig-algorithms/src/vector_search/brute_force_bacalhau/open_data.rs +++ /dev/null @@ -1,113 +0,0 @@ -/*! -Copyright 2024 Louis Silva - -Licensed under the TIG Open Data 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 anyhow::Result; - -use tig_challenges::vector_search::*; - -#[inline] -fn l2_norm(x: &[f32]) -> f32 { - x.iter().map(|&val| val * val).sum::().sqrt() -} - -#[inline] -fn euclidean_distance_with_precomputed_norm( - a_norm_sq: f32, - b_norm_sq: f32, - ab_dot_product: f32 -) -> f32 { - (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() -} - -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let vector_database: &Vec> = &challenge.vector_database; - let query_vectors: &Vec> = &challenge.query_vectors; - let max_distance: f32 = challenge.max_distance; - - let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); - let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); - - let mut sum_norms_sq: f32 = 0.0; - let mut sum_squares: f32 = 0.0; - - for vector in vector_database { - let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); - sum_norms_sq += norm_sq.sqrt(); - sum_squares += norm_sq; - vector_norms_sq.push(norm_sq); - } - - let vector_norms_len: f32 = vector_norms_sq.len() as f32; - let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); - let norm_threshold: f32 = 2.0 * std_dev; - - for query in query_vectors { - let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); - - let mut closest_index: Option = None; - let mut closest_distance: f32 = f32::MAX; - - for (idx, vector) in vector_database.iter().enumerate() { - let vector_norm_sq = vector_norms_sq[idx]; - if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { - continue; - } - - let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); - let distance: f32 = euclidean_distance_with_precomputed_norm( - query_norm_sq, - vector_norm_sq, - ab_dot_product, - ); - - if distance <= max_distance { - closest_index = Some(idx); - break; // Early exit - } else if distance < closest_distance { - closest_index = Some(idx); - closest_distance = distance; - } - } - - if let Some(index) = closest_index { - indexes.push(index); - } else { - return Ok(None); - } - } - - Ok(Some(Solution { indexes })) -} -#[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/vector_search/invector/benchmarker_outbound.rs b/tig-algorithms/src/vector_search/invector/benchmarker_outbound.rs deleted file mode 100644 index c7d3dac..0000000 --- a/tig-algorithms/src/vector_search/invector/benchmarker_outbound.rs +++ /dev/null @@ -1,367 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} - -#[inline(always)] -fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { - let mut sum = 0.0; - for i in 0..180 { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - if sum > c { - sum; - } - for i in 180..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let subset_size = ((max_fuel - base_fuel) / alpha) as usize; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector/commercial.rs b/tig-algorithms/src/vector_search/invector/commercial.rs deleted file mode 100644 index 5ab2af2..0000000 --- a/tig-algorithms/src/vector_search/invector/commercial.rs +++ /dev/null @@ -1,367 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} - -#[inline(always)] -fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { - let mut sum = 0.0; - for i in 0..180 { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - if sum > c { - sum; - } - for i in 180..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let subset_size = ((max_fuel - base_fuel) / alpha) as usize; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector/inbound.rs b/tig-algorithms/src/vector_search/invector/inbound.rs deleted file mode 100644 index bbc5f58..0000000 --- a/tig-algorithms/src/vector_search/invector/inbound.rs +++ /dev/null @@ -1,367 +0,0 @@ -/*! -Copyright 2024 syebastian - -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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} - -#[inline(always)] -fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { - let mut sum = 0.0; - for i in 0..180 { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - if sum > c { - sum; - } - for i in 180..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let subset_size = ((max_fuel - base_fuel) / alpha) as usize; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector/innovator_outbound.rs b/tig-algorithms/src/vector_search/invector/innovator_outbound.rs deleted file mode 100644 index 3496425..0000000 --- a/tig-algorithms/src/vector_search/invector/innovator_outbound.rs +++ /dev/null @@ -1,367 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} - -#[inline(always)] -fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { - let mut sum = 0.0; - for i in 0..180 { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - if sum > c { - sum; - } - for i in 180..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let subset_size = ((max_fuel - base_fuel) / alpha) as usize; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector/mod.rs b/tig-algorithms/src/vector_search/invector/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vector_search/invector/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vector_search/invector/open_data.rs b/tig-algorithms/src/vector_search/invector/open_data.rs deleted file mode 100644 index cada64f..0000000 --- a/tig-algorithms/src/vector_search/invector/open_data.rs +++ /dev/null @@ -1,367 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} - -#[inline(always)] -fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { - let mut sum = 0.0; - for i in 0..180 { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - if sum > c { - sum; - } - for i in 180..a.len() { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - } - sum -} -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let subset_size = ((max_fuel - base_fuel) / alpha) as usize; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector_hybrid/benchmarker_outbound.rs b/tig-algorithms/src/vector_search/invector_hybrid/benchmarker_outbound.rs deleted file mode 100644 index 6a1aa55..0000000 --- a/tig-algorithms/src/vector_search/invector_hybrid/benchmarker_outbound.rs +++ /dev/null @@ -1,395 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0f64; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i] as f64; - } - } - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f64; - } - mean_vector.into_iter().map(|x| x as f32).collect() -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(f32, &'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = early_stopping_distance(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - heap.into_sorted_vec() - .into_iter() - .map(|(FloatOrd(dist), index)| (dist, &database[index][..], index)) - .collect() -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let m = ((max_fuel - base_fuel) / alpha) as usize; - let n = (m as f32 * 1.2) as usize; - let r = n - m; - - let closest_vectors = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - n, - ); - - let (m_slice, r_slice) = closest_vectors.split_at(m); - let m_vectors: Vec<_> = m_slice.to_vec(); - let r_vectors: Vec<_> = r_slice.to_vec(); - - let mut kd_tree_vectors: Vec<(&[f32], usize)> = m_vectors.iter().map(|&(_, v, i)| (v, i)).collect(); - let kd_tree = build_kd_tree(&mut kd_tree_vectors); - - let mut best_indexes = Vec::with_capacity(query_count); - let mut distances = Vec::with_capacity(query_count); - - for query in &challenge.query_vectors { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - distances.push(best.0); - best_indexes.push(best.1.unwrap_or(0)); - } - - let brute_force_count = (query_count as f32 * 0.1) as usize; - let mut distance_indices: Vec<_> = distances.iter().enumerate().collect(); - distance_indices.sort_unstable_by(|a, b| b.1.partial_cmp(a.1).unwrap()); - let high_distance_indices: Vec<_> = distance_indices.into_iter() - .take(brute_force_count) - .map(|(index, _)| index) - .collect(); - - for &query_index in &high_distance_indices { - let query = &challenge.query_vectors[query_index]; - let mut best = (distances[query_index], best_indexes[query_index]); - - for &(_, vec, index) in &r_vectors { - let dist = squared_euclidean_distance(query, vec); - if dist < best.0 { - best = (dist, index); - } - } - - best_indexes[query_index] = best.1; - } - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector_hybrid/commercial.rs b/tig-algorithms/src/vector_search/invector_hybrid/commercial.rs deleted file mode 100644 index c82abc3..0000000 --- a/tig-algorithms/src/vector_search/invector_hybrid/commercial.rs +++ /dev/null @@ -1,395 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0f64; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i] as f64; - } - } - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f64; - } - mean_vector.into_iter().map(|x| x as f32).collect() -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(f32, &'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = early_stopping_distance(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - heap.into_sorted_vec() - .into_iter() - .map(|(FloatOrd(dist), index)| (dist, &database[index][..], index)) - .collect() -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let m = ((max_fuel - base_fuel) / alpha) as usize; - let n = (m as f32 * 1.2) as usize; - let r = n - m; - - let closest_vectors = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - n, - ); - - let (m_slice, r_slice) = closest_vectors.split_at(m); - let m_vectors: Vec<_> = m_slice.to_vec(); - let r_vectors: Vec<_> = r_slice.to_vec(); - - let mut kd_tree_vectors: Vec<(&[f32], usize)> = m_vectors.iter().map(|&(_, v, i)| (v, i)).collect(); - let kd_tree = build_kd_tree(&mut kd_tree_vectors); - - let mut best_indexes = Vec::with_capacity(query_count); - let mut distances = Vec::with_capacity(query_count); - - for query in &challenge.query_vectors { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - distances.push(best.0); - best_indexes.push(best.1.unwrap_or(0)); - } - - let brute_force_count = (query_count as f32 * 0.1) as usize; - let mut distance_indices: Vec<_> = distances.iter().enumerate().collect(); - distance_indices.sort_unstable_by(|a, b| b.1.partial_cmp(a.1).unwrap()); - let high_distance_indices: Vec<_> = distance_indices.into_iter() - .take(brute_force_count) - .map(|(index, _)| index) - .collect(); - - for &query_index in &high_distance_indices { - let query = &challenge.query_vectors[query_index]; - let mut best = (distances[query_index], best_indexes[query_index]); - - for &(_, vec, index) in &r_vectors { - let dist = squared_euclidean_distance(query, vec); - if dist < best.0 { - best = (dist, index); - } - } - - best_indexes[query_index] = best.1; - } - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector_hybrid/inbound.rs b/tig-algorithms/src/vector_search/invector_hybrid/inbound.rs deleted file mode 100644 index 9a283e9..0000000 --- a/tig-algorithms/src/vector_search/invector_hybrid/inbound.rs +++ /dev/null @@ -1,395 +0,0 @@ -/*! -Copyright 2024 syebastian - -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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0f64; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i] as f64; - } - } - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f64; - } - mean_vector.into_iter().map(|x| x as f32).collect() -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(f32, &'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = early_stopping_distance(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - heap.into_sorted_vec() - .into_iter() - .map(|(FloatOrd(dist), index)| (dist, &database[index][..], index)) - .collect() -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let m = ((max_fuel - base_fuel) / alpha) as usize; - let n = (m as f32 * 1.2) as usize; - let r = n - m; - - let closest_vectors = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - n, - ); - - let (m_slice, r_slice) = closest_vectors.split_at(m); - let m_vectors: Vec<_> = m_slice.to_vec(); - let r_vectors: Vec<_> = r_slice.to_vec(); - - let mut kd_tree_vectors: Vec<(&[f32], usize)> = m_vectors.iter().map(|&(_, v, i)| (v, i)).collect(); - let kd_tree = build_kd_tree(&mut kd_tree_vectors); - - let mut best_indexes = Vec::with_capacity(query_count); - let mut distances = Vec::with_capacity(query_count); - - for query in &challenge.query_vectors { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - distances.push(best.0); - best_indexes.push(best.1.unwrap_or(0)); - } - - let brute_force_count = (query_count as f32 * 0.1) as usize; - let mut distance_indices: Vec<_> = distances.iter().enumerate().collect(); - distance_indices.sort_unstable_by(|a, b| b.1.partial_cmp(a.1).unwrap()); - let high_distance_indices: Vec<_> = distance_indices.into_iter() - .take(brute_force_count) - .map(|(index, _)| index) - .collect(); - - for &query_index in &high_distance_indices { - let query = &challenge.query_vectors[query_index]; - let mut best = (distances[query_index], best_indexes[query_index]); - - for &(_, vec, index) in &r_vectors { - let dist = squared_euclidean_distance(query, vec); - if dist < best.0 { - best = (dist, index); - } - } - - best_indexes[query_index] = best.1; - } - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector_hybrid/innovator_outbound.rs b/tig-algorithms/src/vector_search/invector_hybrid/innovator_outbound.rs deleted file mode 100644 index 624671f..0000000 --- a/tig-algorithms/src/vector_search/invector_hybrid/innovator_outbound.rs +++ /dev/null @@ -1,395 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0f64; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i] as f64; - } - } - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f64; - } - mean_vector.into_iter().map(|x| x as f32).collect() -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(f32, &'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = early_stopping_distance(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - heap.into_sorted_vec() - .into_iter() - .map(|(FloatOrd(dist), index)| (dist, &database[index][..], index)) - .collect() -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let m = ((max_fuel - base_fuel) / alpha) as usize; - let n = (m as f32 * 1.2) as usize; - let r = n - m; - - let closest_vectors = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - n, - ); - - let (m_slice, r_slice) = closest_vectors.split_at(m); - let m_vectors: Vec<_> = m_slice.to_vec(); - let r_vectors: Vec<_> = r_slice.to_vec(); - - let mut kd_tree_vectors: Vec<(&[f32], usize)> = m_vectors.iter().map(|&(_, v, i)| (v, i)).collect(); - let kd_tree = build_kd_tree(&mut kd_tree_vectors); - - let mut best_indexes = Vec::with_capacity(query_count); - let mut distances = Vec::with_capacity(query_count); - - for query in &challenge.query_vectors { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - distances.push(best.0); - best_indexes.push(best.1.unwrap_or(0)); - } - - let brute_force_count = (query_count as f32 * 0.1) as usize; - let mut distance_indices: Vec<_> = distances.iter().enumerate().collect(); - distance_indices.sort_unstable_by(|a, b| b.1.partial_cmp(a.1).unwrap()); - let high_distance_indices: Vec<_> = distance_indices.into_iter() - .take(brute_force_count) - .map(|(index, _)| index) - .collect(); - - for &query_index in &high_distance_indices { - let query = &challenge.query_vectors[query_index]; - let mut best = (distances[query_index], best_indexes[query_index]); - - for &(_, vec, index) in &r_vectors { - let dist = squared_euclidean_distance(query, vec); - if dist < best.0 { - best = (dist, index); - } - } - - best_indexes[query_index] = best.1; - } - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/invector_hybrid/mod.rs b/tig-algorithms/src/vector_search/invector_hybrid/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vector_search/invector_hybrid/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vector_search/invector_hybrid/open_data.rs b/tig-algorithms/src/vector_search/invector_hybrid/open_data.rs deleted file mode 100644 index e528466..0000000 --- a/tig-algorithms/src/vector_search/invector_hybrid/open_data.rs +++ /dev/null @@ -1,395 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - let len = a.len(); - - if a.len() != b.len() || a.len() < 8 { - return f32::MAX; - } - - while i + 7 < len { - unsafe { - let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); - let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); - let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); - let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); - let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); - let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); - let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); - let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + - diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; - } - - if sum > current_min { - return f32::MAX; - } - - i += 8; - } - - while i < len { - unsafe { - let diff = *a.get_unchecked(i) - *b.get_unchecked(i); - sum += diff * diff; - } - i += 1; - } - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - let (nearer, farther) = if diff < 0.0 { - (&node.left, &node.right) - } else { - (&node.right, &node.left) - }; - - if let Some(nearer_node) = nearer { - stack.push((nearer_node.as_ref(), depth + 1)); - } - - if sqr_diff < best.0 { - if let Some(farther_node) = farther { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - } -} -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0f64; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i] as f64; - } - } - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f64; - } - mean_vector.into_iter().map(|x| x as f32).collect() -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(f32, &'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - if heap.len() < k - { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() - { - let dist = early_stopping_distance(&mean_query_vector, vector, top_dist); - let ord_dist = FloatOrd(dist); - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - heap.into_sorted_vec() - .into_iter() - .map(|(FloatOrd(dist), index)| (dist, &database[index][..], index)) - .collect() -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let max_fuel = 2000000000.0; - let base_fuel = 760000000.0; - let alpha = 1700.0 * challenge.difficulty.num_queries as f64; - - let m = ((max_fuel - base_fuel) / alpha) as usize; - let n = (m as f32 * 1.2) as usize; - let r = n - m; - - let closest_vectors = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - n, - ); - - let (m_slice, r_slice) = closest_vectors.split_at(m); - let m_vectors: Vec<_> = m_slice.to_vec(); - let r_vectors: Vec<_> = r_slice.to_vec(); - - let mut kd_tree_vectors: Vec<(&[f32], usize)> = m_vectors.iter().map(|&(_, v, i)| (v, i)).collect(); - let kd_tree = build_kd_tree(&mut kd_tree_vectors); - - let mut best_indexes = Vec::with_capacity(query_count); - let mut distances = Vec::with_capacity(query_count); - - for query in &challenge.query_vectors { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - distances.push(best.0); - best_indexes.push(best.1.unwrap_or(0)); - } - - let brute_force_count = (query_count as f32 * 0.1) as usize; - let mut distance_indices: Vec<_> = distances.iter().enumerate().collect(); - distance_indices.sort_unstable_by(|a, b| b.1.partial_cmp(a.1).unwrap()); - let high_distance_indices: Vec<_> = distance_indices.into_iter() - .take(brute_force_count) - .map(|(index, _)| index) - .collect(); - - for &query_index in &high_distance_indices { - let query = &challenge.query_vectors[query_index]; - let mut best = (distances[query_index], best_indexes[query_index]); - - for &(_, vec, index) in &r_vectors { - let dist = squared_euclidean_distance(query, vec); - if dist < best.0 { - best = (dist, index); - } - } - - best_indexes[query_index] = best.1; - } - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[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/vector_search/mod.rs b/tig-algorithms/src/vector_search/mod.rs index cee7ac5..ae3472a 100644 --- a/tig-algorithms/src/vector_search/mod.rs +++ b/tig-algorithms/src/vector_search/mod.rs @@ -24,8 +24,7 @@ // c004_a013 -pub mod brute_force_bacalhau; -pub use brute_force_bacalhau as c004_a014; +// c004_a014 // c004_a015 @@ -49,8 +48,7 @@ pub use brute_force_bacalhau as c004_a014; // c004_a025 -pub mod optimax_gpu; -pub use optimax_gpu as c004_a026; +// c004_a026 // c004_a027 @@ -66,8 +64,7 @@ pub use optimax_gpu as c004_a026; // c004_a033 -pub mod invector; -pub use invector as c004_a034; +// c004_a034 // c004_a035 @@ -83,8 +80,7 @@ pub use invector as c004_a034; // c004_a041 -pub mod invector_hybrid; -pub use invector_hybrid as c004_a042; +// c004_a042 // c004_a043 diff --git a/tig-algorithms/src/vector_search/optimax_gpu/benchmarker_outbound.rs b/tig-algorithms/src/vector_search/optimax_gpu/benchmarker_outbound.rs deleted file mode 100644 index dd9b614..0000000 --- a/tig-algorithms/src/vector_search/optimax_gpu/benchmarker_outbound.rs +++ /dev/null @@ -1,468 +0,0 @@ -/*! -Copyright 2024 bw-dev36 - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - while i + 3 < a.len() { - let diff0 = a[i] - b[i]; - let diff1 = a[i + 1] - b[i + 1]; - let diff2 = a[i + 2] - b[i + 2]; - let diff3 = a[i + 3] - b[i + 3]; - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; - - if sum > current_min { - return f32::MAX; - } - - i += 4; - } - - while i < a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - - if sum > current_min { - return f32::MAX; - } - - i += 1; - } - - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - if sqr_diff < best.0 { - if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - - if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { - stack.push((nearer_node.as_ref(), depth + 1)); - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - pub const KERNEL: Option = Some(CudaKernel { - src: r#" - - extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < num_vectors) { - float dist = 0.0; - for (int d = 0; d < num_dimensions; ++d) { - float diff = query_mean[d] - vectors[idx * num_dimensions + d]; - dist += diff * diff; - } - distances[idx] = dist; - } - } - - "#, - - funcs: &["filter_vectors"], - }); - - pub fn cuda_solve_challenge( - challenge: &Challenge, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = cuda_filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - dev, - funcs, - )?; - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - - - - Ok(Some(Solution { - indexes: best_indexes, - })) - } - - #[cfg(feature = "cuda")] - fn cuda_filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let num_vectors = database.len(); - let num_dimensions = 250; - let flattened_database: Vec = database.iter().flatten().cloned().collect(); - let database_dev = dev.htod_sync_copy(&flattened_database)?; - let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; - let mut distances_dev = dev.alloc_zeros::(num_vectors)?; - let cfg = LaunchConfig { - block_dim: (256, 1, 1), - grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), - shared_mem_bytes: 0, - }; - unsafe { - funcs.remove("filter_vectors").unwrap().launch( - cfg, - ( - &mean_query_dev, - &database_dev, - &mut distances_dev, - num_vectors as i32, - num_dimensions as i32, - ), - ) - }?; - let mut distances_host = vec![0.0f32; num_vectors]; - dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, &distance) in distances_host.iter().enumerate() { - let ord_dist = FloatOrd(distance); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if distance < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&[f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - Ok(result) - } - - #[cfg(feature = "cuda")] - fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> Option>> { - None - } - - #[cfg(feature = "cuda")] - fn cuda_nearest_neighbor_search( - kd_tree: &Option>>, - query: &[f32], - best: &mut (f32, Option), - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result<()> { - Ok(()) - } -} -#[cfg(feature = "cuda")] -pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/optimax_gpu/commercial.rs b/tig-algorithms/src/vector_search/optimax_gpu/commercial.rs deleted file mode 100644 index 4bdaec5..0000000 --- a/tig-algorithms/src/vector_search/optimax_gpu/commercial.rs +++ /dev/null @@ -1,468 +0,0 @@ -/*! -Copyright 2024 bw-dev36 - -Licensed under the TIG Commercial License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - while i + 3 < a.len() { - let diff0 = a[i] - b[i]; - let diff1 = a[i + 1] - b[i + 1]; - let diff2 = a[i + 2] - b[i + 2]; - let diff3 = a[i + 3] - b[i + 3]; - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; - - if sum > current_min { - return f32::MAX; - } - - i += 4; - } - - while i < a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - - if sum > current_min { - return f32::MAX; - } - - i += 1; - } - - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - if sqr_diff < best.0 { - if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - - if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { - stack.push((nearer_node.as_ref(), depth + 1)); - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - pub const KERNEL: Option = Some(CudaKernel { - src: r#" - - extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < num_vectors) { - float dist = 0.0; - for (int d = 0; d < num_dimensions; ++d) { - float diff = query_mean[d] - vectors[idx * num_dimensions + d]; - dist += diff * diff; - } - distances[idx] = dist; - } - } - - "#, - - funcs: &["filter_vectors"], - }); - - pub fn cuda_solve_challenge( - challenge: &Challenge, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = cuda_filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - dev, - funcs, - )?; - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - - - - Ok(Some(Solution { - indexes: best_indexes, - })) - } - - #[cfg(feature = "cuda")] - fn cuda_filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let num_vectors = database.len(); - let num_dimensions = 250; - let flattened_database: Vec = database.iter().flatten().cloned().collect(); - let database_dev = dev.htod_sync_copy(&flattened_database)?; - let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; - let mut distances_dev = dev.alloc_zeros::(num_vectors)?; - let cfg = LaunchConfig { - block_dim: (256, 1, 1), - grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), - shared_mem_bytes: 0, - }; - unsafe { - funcs.remove("filter_vectors").unwrap().launch( - cfg, - ( - &mean_query_dev, - &database_dev, - &mut distances_dev, - num_vectors as i32, - num_dimensions as i32, - ), - ) - }?; - let mut distances_host = vec![0.0f32; num_vectors]; - dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, &distance) in distances_host.iter().enumerate() { - let ord_dist = FloatOrd(distance); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if distance < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&[f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - Ok(result) - } - - #[cfg(feature = "cuda")] - fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> Option>> { - None - } - - #[cfg(feature = "cuda")] - fn cuda_nearest_neighbor_search( - kd_tree: &Option>>, - query: &[f32], - best: &mut (f32, Option), - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result<()> { - Ok(()) - } -} -#[cfg(feature = "cuda")] -pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/optimax_gpu/inbound.rs b/tig-algorithms/src/vector_search/optimax_gpu/inbound.rs deleted file mode 100644 index 23ae821..0000000 --- a/tig-algorithms/src/vector_search/optimax_gpu/inbound.rs +++ /dev/null @@ -1,468 +0,0 @@ -/*! -Copyright 2024 bw-dev36 - -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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - while i + 3 < a.len() { - let diff0 = a[i] - b[i]; - let diff1 = a[i + 1] - b[i + 1]; - let diff2 = a[i + 2] - b[i + 2]; - let diff3 = a[i + 3] - b[i + 3]; - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; - - if sum > current_min { - return f32::MAX; - } - - i += 4; - } - - while i < a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - - if sum > current_min { - return f32::MAX; - } - - i += 1; - } - - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - if sqr_diff < best.0 { - if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - - if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { - stack.push((nearer_node.as_ref(), depth + 1)); - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - pub const KERNEL: Option = Some(CudaKernel { - src: r#" - - extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < num_vectors) { - float dist = 0.0; - for (int d = 0; d < num_dimensions; ++d) { - float diff = query_mean[d] - vectors[idx * num_dimensions + d]; - dist += diff * diff; - } - distances[idx] = dist; - } - } - - "#, - - funcs: &["filter_vectors"], - }); - - pub fn cuda_solve_challenge( - challenge: &Challenge, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = cuda_filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - dev, - funcs, - )?; - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - - - - Ok(Some(Solution { - indexes: best_indexes, - })) - } - - #[cfg(feature = "cuda")] - fn cuda_filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let num_vectors = database.len(); - let num_dimensions = 250; - let flattened_database: Vec = database.iter().flatten().cloned().collect(); - let database_dev = dev.htod_sync_copy(&flattened_database)?; - let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; - let mut distances_dev = dev.alloc_zeros::(num_vectors)?; - let cfg = LaunchConfig { - block_dim: (256, 1, 1), - grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), - shared_mem_bytes: 0, - }; - unsafe { - funcs.remove("filter_vectors").unwrap().launch( - cfg, - ( - &mean_query_dev, - &database_dev, - &mut distances_dev, - num_vectors as i32, - num_dimensions as i32, - ), - ) - }?; - let mut distances_host = vec![0.0f32; num_vectors]; - dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, &distance) in distances_host.iter().enumerate() { - let ord_dist = FloatOrd(distance); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if distance < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&[f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - Ok(result) - } - - #[cfg(feature = "cuda")] - fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> Option>> { - None - } - - #[cfg(feature = "cuda")] - fn cuda_nearest_neighbor_search( - kd_tree: &Option>>, - query: &[f32], - best: &mut (f32, Option), - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result<()> { - Ok(()) - } -} -#[cfg(feature = "cuda")] -pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/optimax_gpu/innovator_outbound.rs b/tig-algorithms/src/vector_search/optimax_gpu/innovator_outbound.rs deleted file mode 100644 index dc1c7e1..0000000 --- a/tig-algorithms/src/vector_search/optimax_gpu/innovator_outbound.rs +++ /dev/null @@ -1,468 +0,0 @@ -/*! -Copyright 2024 bw-dev36 - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - while i + 3 < a.len() { - let diff0 = a[i] - b[i]; - let diff1 = a[i + 1] - b[i + 1]; - let diff2 = a[i + 2] - b[i + 2]; - let diff3 = a[i + 3] - b[i + 3]; - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; - - if sum > current_min { - return f32::MAX; - } - - i += 4; - } - - while i < a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - - if sum > current_min { - return f32::MAX; - } - - i += 1; - } - - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - if sqr_diff < best.0 { - if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - - if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { - stack.push((nearer_node.as_ref(), depth + 1)); - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - pub const KERNEL: Option = Some(CudaKernel { - src: r#" - - extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < num_vectors) { - float dist = 0.0; - for (int d = 0; d < num_dimensions; ++d) { - float diff = query_mean[d] - vectors[idx * num_dimensions + d]; - dist += diff * diff; - } - distances[idx] = dist; - } - } - - "#, - - funcs: &["filter_vectors"], - }); - - pub fn cuda_solve_challenge( - challenge: &Challenge, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = cuda_filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - dev, - funcs, - )?; - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - - - - Ok(Some(Solution { - indexes: best_indexes, - })) - } - - #[cfg(feature = "cuda")] - fn cuda_filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let num_vectors = database.len(); - let num_dimensions = 250; - let flattened_database: Vec = database.iter().flatten().cloned().collect(); - let database_dev = dev.htod_sync_copy(&flattened_database)?; - let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; - let mut distances_dev = dev.alloc_zeros::(num_vectors)?; - let cfg = LaunchConfig { - block_dim: (256, 1, 1), - grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), - shared_mem_bytes: 0, - }; - unsafe { - funcs.remove("filter_vectors").unwrap().launch( - cfg, - ( - &mean_query_dev, - &database_dev, - &mut distances_dev, - num_vectors as i32, - num_dimensions as i32, - ), - ) - }?; - let mut distances_host = vec![0.0f32; num_vectors]; - dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, &distance) in distances_host.iter().enumerate() { - let ord_dist = FloatOrd(distance); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if distance < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&[f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - Ok(result) - } - - #[cfg(feature = "cuda")] - fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> Option>> { - None - } - - #[cfg(feature = "cuda")] - fn cuda_nearest_neighbor_search( - kd_tree: &Option>>, - query: &[f32], - best: &mut (f32, Option), - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result<()> { - Ok(()) - } -} -#[cfg(feature = "cuda")] -pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/optimax_gpu/mod.rs b/tig-algorithms/src/vector_search/optimax_gpu/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vector_search/optimax_gpu/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vector_search/optimax_gpu/open_data.rs b/tig-algorithms/src/vector_search/optimax_gpu/open_data.rs deleted file mode 100644 index 37097ab..0000000 --- a/tig-algorithms/src/vector_search/optimax_gpu/open_data.rs +++ /dev/null @@ -1,468 +0,0 @@ -/*! -Copyright 2024 bw-dev36 - -Licensed under the TIG Open Data 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 anyhow::Ok; -use tig_challenges::vector_search::*; -use std::cmp::Ordering; -use std::collections::BinaryHeap; - -struct KDNode<'a> { - point: &'a [f32], - left: Option>>, - right: Option>>, - index: usize, -} - -impl<'a> KDNode<'a> { - fn new(point: &'a [f32], index: usize) -> Self { - KDNode { - point, - left: None, - right: None, - index, - } - } -} -fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - if arr.len() <= 1 { - return; - } - - let pivot_index = partition(arr, compare); - if k < pivot_index { - quickselect_by(&mut arr[..pivot_index], k, compare); - } else if k > pivot_index { - quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); - } -} - -fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize -where - F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, -{ - let pivot_index = arr.len() >> 1; - arr.swap(pivot_index, arr.len() - 1); - - let mut store_index = 0; - for i in 0..arr.len() - 1 { - if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { - arr.swap(i, store_index); - store_index += 1; - } - } - arr.swap(store_index, arr.len() - 1); - store_index -} - -fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { - if points.is_empty() { - return None; - } - - const NUM_DIMENSIONS: usize = 250; - let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); - let mut root: Option>> = None; - - stack.push((0, points.len(), 0, None, false)); - - while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { - if start >= end { - continue; - } - - let axis = depth % NUM_DIMENSIONS; - let median = (start + end) / 2; - quickselect_by(&mut points[start..end], median - start, &|a, b| { - a.0[axis].partial_cmp(&b.0[axis]).unwrap() - }); - - let (median_point, median_index) = points[median]; - let mut new_node = Box::new(KDNode::new(median_point, median_index)); - let new_node_ptr: *mut KDNode = &mut *new_node; - - if let Some(parent_ptr) = parent_ptr { - unsafe { - if is_left { - (*parent_ptr).left = Some(new_node); - } else { - (*parent_ptr).right = Some(new_node); - } - } - } else { - root = Some(new_node); - } - - stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); - stack.push((start, median, depth + 1, Some(new_node_ptr), true)); - } - - root -} - -#[inline(always)] -fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { - let mut sum = 0.0; - for i in 0..a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - } - sum -} - -#[inline(always)] -fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { - let mut sum = 0.0; - let mut i = 0; - while i + 3 < a.len() { - let diff0 = a[i] - b[i]; - let diff1 = a[i + 1] - b[i + 1]; - let diff2 = a[i + 2] - b[i + 2]; - let diff3 = a[i + 3] - b[i + 3]; - - sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; - - if sum > current_min { - return f32::MAX; - } - - i += 4; - } - - while i < a.len() { - let diff = a[i] - b[i]; - sum += diff * diff; - - if sum > current_min { - return f32::MAX; - } - - i += 1; - } - - sum -} - -fn nearest_neighbor_search<'a>( - root: &Option>>, - target: &[f32], - best: &mut (f32, Option), -) { - let num_dimensions = target.len(); - let mut stack = Vec::with_capacity(64); - - if let Some(node) = root { - stack.push((node.as_ref(), 0)); - } - - while let Some((node, depth)) = stack.pop() { - let axis = depth % num_dimensions; - let dist = early_stopping_distance(&node.point, target, best.0); - - if dist < best.0 { - best.0 = dist; - best.1 = Some(node.index); - } - - let diff = target[axis] - node.point[axis]; - let sqr_diff = diff * diff; - - if sqr_diff < best.0 { - if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { - stack.push((farther_node.as_ref(), depth + 1)); - } - } - - if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { - stack.push((nearer_node.as_ref(), depth + 1)); - } - } -} - -fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { - let num_vectors = vectors.len(); - let num_dimensions = 250; - - let mut mean_vector = vec![0.0; num_dimensions]; - - for vector in vectors { - for i in 0..num_dimensions { - mean_vector[i] += vector[i]; - } - } - - for i in 0..num_dimensions { - mean_vector[i] /= num_vectors as f32; - } - - mean_vector -} - -#[derive(Debug)] -struct FloatOrd(f32); - -impl PartialEq for FloatOrd { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for FloatOrd {} - -impl PartialOrd for FloatOrd { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Ord for FloatOrd { - fn cmp(&self, other: &Self) -> Ordering { - - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -fn filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, -) -> Vec<(&'a [f32], usize)> { - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, vector) in database.iter().enumerate() { - let dist = squared_euclidean_distance(&mean_query_vector, vector); - let ord_dist = FloatOrd(dist); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if dist < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&'a [f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - result -} - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - ); - - - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - Ok(Some(Solution { - indexes: best_indexes, - })) -} - -#[cfg(feature = "cuda")] -mod gpu_optimisation { - use super::*; - use cudarc::driver::*; - use std::{collections::HashMap, sync::Arc}; - use tig_challenges::CudaKernel; - pub const KERNEL: Option = Some(CudaKernel { - src: r#" - - extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < num_vectors) { - float dist = 0.0; - for (int d = 0; d < num_dimensions; ++d) { - float diff = query_mean[d] - vectors[idx * num_dimensions + d]; - dist += diff * diff; - } - distances[idx] = dist; - } - } - - "#, - - funcs: &["filter_vectors"], - }); - - pub fn cuda_solve_challenge( - challenge: &Challenge, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - let query_count = challenge.query_vectors.len(); - - let subset_size = match query_count { - 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, - 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, - 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, - 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel - 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, - 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, - 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel - 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, - 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel - 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, - 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel - _ => 1000, // need more fuel - }; - let subset = cuda_filter_relevant_vectors( - &challenge.vector_database, - &challenge.query_vectors, - subset_size, - dev, - funcs, - )?; - let kd_tree = build_kd_tree(&mut subset.clone()); - - - let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); - - for query in challenge.query_vectors.iter() { - let mut best = (std::f32::MAX, None); - nearest_neighbor_search(&kd_tree, query, &mut best); - - if let Some(best_index) = best.1 { - best_indexes.push(best_index); - } - } - - - - - - Ok(Some(Solution { - indexes: best_indexes, - })) - } - - #[cfg(feature = "cuda")] - fn cuda_filter_relevant_vectors<'a>( - database: &'a [Vec], - query_vectors: &[Vec], - k: usize, - dev: &Arc, - mut funcs: HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result> { - - let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); - let mean_query_vector = calculate_mean_vector(&query_refs); - - let num_vectors = database.len(); - let num_dimensions = 250; - let flattened_database: Vec = database.iter().flatten().cloned().collect(); - let database_dev = dev.htod_sync_copy(&flattened_database)?; - let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; - let mut distances_dev = dev.alloc_zeros::(num_vectors)?; - let cfg = LaunchConfig { - block_dim: (256, 1, 1), - grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), - shared_mem_bytes: 0, - }; - unsafe { - funcs.remove("filter_vectors").unwrap().launch( - cfg, - ( - &mean_query_dev, - &database_dev, - &mut distances_dev, - num_vectors as i32, - num_dimensions as i32, - ), - ) - }?; - let mut distances_host = vec![0.0f32; num_vectors]; - dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; - let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); - - for (index, &distance) in distances_host.iter().enumerate() { - let ord_dist = FloatOrd(distance); - if heap.len() < k { - heap.push((ord_dist, index)); - } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { - if distance < top_dist { - heap.pop(); - heap.push((ord_dist, index)); - } - } - } - let result: Vec<(&[f32], usize)> = heap - .into_iter() - .map(|(_, index)| (&database[index][..], index)) - .collect(); - - Ok(result) - } - - #[cfg(feature = "cuda")] - fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> Option>> { - None - } - - #[cfg(feature = "cuda")] - fn cuda_nearest_neighbor_search( - kd_tree: &Option>>, - query: &[f32], - best: &mut (f32, Option), - dev: &Arc, - funcs: &mut HashMap<&'static str, CudaFunction>, - ) -> anyhow::Result<()> { - Ok(()) - } -} -#[cfg(feature = "cuda")] -pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/template.rs b/tig-algorithms/src/vector_search/template.rs index 0f5fa1e..eddf8a0 100644 --- a/tig-algorithms/src/vector_search/template.rs +++ b/tig-algorithms/src/vector_search/template.rs @@ -1,7 +1,11 @@ /*! -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.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 @@ -13,6 +17,24 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the speci language governing permissions and limitations under the License. */ +// REMOVE BELOW SECTION IF UNUSED +/* +REFERENCES AND ACKNOWLEDGMENTS + +This implementation is based on or inspired by existing work. Citations and +acknowledgments below: + +1. Academic Papers: + - [Author(s), "Paper Title", DOI (if available)] + +2. Code References: + - [Author(s), URL] + +3. Other: + - [Author(s), Details] + +*/ + // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge use anyhow::{anyhow, Result}; use tig_challenges::vector_search::{Challenge, Solution}; diff --git a/tig-algorithms/src/vehicle_routing/advanced_heuristics/benchmarker_outbound.rs b/tig-algorithms/src/vehicle_routing/advanced_heuristics/benchmarker_outbound.rs deleted file mode 100644 index 09f6324..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_heuristics/benchmarker_outbound.rs +++ /dev/null @@ -1,240 +0,0 @@ -/*! -Copyright 2024 CodeAlchemist - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_heuristics/commercial.rs b/tig-algorithms/src/vehicle_routing/advanced_heuristics/commercial.rs deleted file mode 100644 index 3a77824..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_heuristics/commercial.rs +++ /dev/null @@ -1,240 +0,0 @@ -/*! -Copyright 2024 CodeAlchemist - -Licensed under the TIG Commercial License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_heuristics/inbound.rs b/tig-algorithms/src/vehicle_routing/advanced_heuristics/inbound.rs deleted file mode 100644 index a666f12..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_heuristics/inbound.rs +++ /dev/null @@ -1,240 +0,0 @@ -/*! -Copyright 2024 CodeAlchemist - -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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_heuristics/innovator_outbound.rs b/tig-algorithms/src/vehicle_routing/advanced_heuristics/innovator_outbound.rs deleted file mode 100644 index 35ca488..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_heuristics/innovator_outbound.rs +++ /dev/null @@ -1,240 +0,0 @@ -/*! -Copyright 2024 CodeAlchemist - -Licensed under the TIG Innovator Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_heuristics/mod.rs b/tig-algorithms/src/vehicle_routing/advanced_heuristics/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_heuristics/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_heuristics/open_data.rs b/tig-algorithms/src/vehicle_routing/advanced_heuristics/open_data.rs deleted file mode 100644 index 2fc0fee..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_heuristics/open_data.rs +++ /dev/null @@ -1,240 +0,0 @@ -/*! -Copyright 2024 CodeAlchemist - -Licensed under the TIG Open Data 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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_routing/benchmarker_outbound.rs b/tig-algorithms/src/vehicle_routing/advanced_routing/benchmarker_outbound.rs deleted file mode 100644 index 8d7001f..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_routing/benchmarker_outbound.rs +++ /dev/null @@ -1,234 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_routing/commercial.rs b/tig-algorithms/src/vehicle_routing/advanced_routing/commercial.rs deleted file mode 100644 index bb1110f..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_routing/commercial.rs +++ /dev/null @@ -1,234 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_routing/inbound.rs b/tig-algorithms/src/vehicle_routing/advanced_routing/inbound.rs deleted file mode 100644 index 2b8f8e1..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_routing/inbound.rs +++ /dev/null @@ -1,234 +0,0 @@ -/*! -Copyright 2024 syebastian - -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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_routing/innovator_outbound.rs b/tig-algorithms/src/vehicle_routing/advanced_routing/innovator_outbound.rs deleted file mode 100644 index e768ec0..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_routing/innovator_outbound.rs +++ /dev/null @@ -1,234 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_routing/mod.rs b/tig-algorithms/src/vehicle_routing/advanced_routing/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_routing/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/advanced_routing/open_data.rs b/tig-algorithms/src/vehicle_routing/advanced_routing/open_data.rs deleted file mode 100644 index 41bdafe..0000000 --- a/tig-algorithms/src/vehicle_routing/advanced_routing/open_data.rs +++ /dev/null @@ -1,234 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - savings.push((0.0, i as u8, j as u8)); - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright/benchmarker_outbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright/benchmarker_outbound.rs deleted file mode 100644 index f6fdcb7..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright/benchmarker_outbound.rs +++ /dev/null @@ -1,124 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::new(); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - scores.sort_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - Ok(Some(Solution { - routes: routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(), - })) -} - -#[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/vehicle_routing/clarke_wright/commercial.rs b/tig-algorithms/src/vehicle_routing/clarke_wright/commercial.rs deleted file mode 100644 index 03acd66..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright/commercial.rs +++ /dev/null @@ -1,124 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Commercial License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::new(); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - scores.sort_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - Ok(Some(Solution { - routes: routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(), - })) -} - -#[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/vehicle_routing/clarke_wright/inbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright/inbound.rs deleted file mode 100644 index 728e063..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright/inbound.rs +++ /dev/null @@ -1,124 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::new(); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - scores.sort_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - Ok(Some(Solution { - routes: routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(), - })) -} - -#[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/vehicle_routing/clarke_wright/innovator_outbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright/innovator_outbound.rs deleted file mode 100644 index d523425..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright/innovator_outbound.rs +++ /dev/null @@ -1,124 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::new(); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - scores.sort_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - Ok(Some(Solution { - routes: routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(), - })) -} - -#[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/vehicle_routing/clarke_wright/mod.rs b/tig-algorithms/src/vehicle_routing/clarke_wright/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright/open_data.rs b/tig-algorithms/src/vehicle_routing/clarke_wright/open_data.rs deleted file mode 100644 index 101edeb..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright/open_data.rs +++ /dev/null @@ -1,124 +0,0 @@ -/*! -Copyright 2024 Uncharted Trading Limited - -Licensed under the TIG Open Data 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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::new(); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - scores.sort_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - Ok(Some(Solution { - routes: routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(), - })) -} - -#[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/vehicle_routing/clarke_wright_super/benchmarker_outbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/benchmarker_outbound.rs deleted file mode 100644 index cb49161..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright_super/benchmarker_outbound.rs +++ /dev/null @@ -1,130 +0,0 @@ -/*! -Copyright 2024 OvErLoDe - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); -for i in 1..n { - let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups - for j in (i + 1)..n { - let score = d_i0 + d[0][j] - d[i][j]; - scores.push((score, i, j)); - } -} - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - // Directly get the routes - let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); - - // Cache indices and demands - let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); - let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - // Check constraints - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - // Merge routes - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // Reverse if needed - if left_startnode == i { - left_route.reverse(); - } - if right_endnode == j { - right_route.reverse(); - } - - // Create new route - let mut new_route = left_route; - new_route.extend(right_route); - - // Update routes and demands - let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); - routes[start] = Some(new_route.clone()); - routes[end] = Some(new_route); - route_demands[start] = merged_demand; - route_demands[end] = merged_demand; - } - - let mut final_routes = Vec::new(); - - for (i, opt_route) in routes.into_iter().enumerate() { - if let Some(mut route) = opt_route { - if route[0] == i { - let mut full_route = Vec::with_capacity(route.len() + 2); - full_route.push(0); - full_route.append(&mut route); - full_route.push(0); - final_routes.push(full_route); - } - } - } - - Ok(Some(Solution { routes: final_routes })) - -} -#[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/vehicle_routing/clarke_wright_super/commercial.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/commercial.rs deleted file mode 100644 index 257991d..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright_super/commercial.rs +++ /dev/null @@ -1,130 +0,0 @@ -/*! -Copyright 2024 OvErLoDe - -Licensed under the TIG Commercial License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); -for i in 1..n { - let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups - for j in (i + 1)..n { - let score = d_i0 + d[0][j] - d[i][j]; - scores.push((score, i, j)); - } -} - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - // Directly get the routes - let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); - - // Cache indices and demands - let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); - let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - // Check constraints - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - // Merge routes - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // Reverse if needed - if left_startnode == i { - left_route.reverse(); - } - if right_endnode == j { - right_route.reverse(); - } - - // Create new route - let mut new_route = left_route; - new_route.extend(right_route); - - // Update routes and demands - let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); - routes[start] = Some(new_route.clone()); - routes[end] = Some(new_route); - route_demands[start] = merged_demand; - route_demands[end] = merged_demand; - } - - let mut final_routes = Vec::new(); - - for (i, opt_route) in routes.into_iter().enumerate() { - if let Some(mut route) = opt_route { - if route[0] == i { - let mut full_route = Vec::with_capacity(route.len() + 2); - full_route.push(0); - full_route.append(&mut route); - full_route.push(0); - final_routes.push(full_route); - } - } - } - - Ok(Some(Solution { routes: final_routes })) - -} -#[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/vehicle_routing/clarke_wright_super/inbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/inbound.rs deleted file mode 100644 index 0523dd0..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright_super/inbound.rs +++ /dev/null @@ -1,130 +0,0 @@ -/*! -Copyright 2024 OvErLoDe - -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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); -for i in 1..n { - let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups - for j in (i + 1)..n { - let score = d_i0 + d[0][j] - d[i][j]; - scores.push((score, i, j)); - } -} - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - // Directly get the routes - let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); - - // Cache indices and demands - let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); - let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - // Check constraints - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - // Merge routes - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // Reverse if needed - if left_startnode == i { - left_route.reverse(); - } - if right_endnode == j { - right_route.reverse(); - } - - // Create new route - let mut new_route = left_route; - new_route.extend(right_route); - - // Update routes and demands - let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); - routes[start] = Some(new_route.clone()); - routes[end] = Some(new_route); - route_demands[start] = merged_demand; - route_demands[end] = merged_demand; - } - - let mut final_routes = Vec::new(); - - for (i, opt_route) in routes.into_iter().enumerate() { - if let Some(mut route) = opt_route { - if route[0] == i { - let mut full_route = Vec::with_capacity(route.len() + 2); - full_route.push(0); - full_route.append(&mut route); - full_route.push(0); - final_routes.push(full_route); - } - } - } - - Ok(Some(Solution { routes: final_routes })) - -} -#[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/vehicle_routing/clarke_wright_super/innovator_outbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/innovator_outbound.rs deleted file mode 100644 index c87bd18..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright_super/innovator_outbound.rs +++ /dev/null @@ -1,130 +0,0 @@ -/*! -Copyright 2024 OvErLoDe - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); -for i in 1..n { - let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups - for j in (i + 1)..n { - let score = d_i0 + d[0][j] - d[i][j]; - scores.push((score, i, j)); - } -} - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - // Directly get the routes - let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); - - // Cache indices and demands - let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); - let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - // Check constraints - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - // Merge routes - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // Reverse if needed - if left_startnode == i { - left_route.reverse(); - } - if right_endnode == j { - right_route.reverse(); - } - - // Create new route - let mut new_route = left_route; - new_route.extend(right_route); - - // Update routes and demands - let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); - routes[start] = Some(new_route.clone()); - routes[end] = Some(new_route); - route_demands[start] = merged_demand; - route_demands[end] = merged_demand; - } - - let mut final_routes = Vec::new(); - - for (i, opt_route) in routes.into_iter().enumerate() { - if let Some(mut route) = opt_route { - if route[0] == i { - let mut full_route = Vec::with_capacity(route.len() + 2); - full_route.push(0); - full_route.append(&mut route); - full_route.push(0); - final_routes.push(full_route); - } - } - } - - Ok(Some(Solution { routes: final_routes })) - -} -#[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/vehicle_routing/clarke_wright_super/mod.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright_super/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright_super/open_data.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/open_data.rs deleted file mode 100644 index 5d71dbb..0000000 --- a/tig-algorithms/src/vehicle_routing/clarke_wright_super/open_data.rs +++ /dev/null @@ -1,130 +0,0 @@ -/*! -Copyright 2024 OvErLoDe - -Licensed under the TIG Open Data 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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); -for i in 1..n { - let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups - for j in (i + 1)..n { - let score = d_i0 + d[0][j] - d[i][j]; - scores.push((score, i, j)); - } -} - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - // Directly get the routes - let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); - - // Cache indices and demands - let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); - let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - // Check constraints - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - // Merge routes - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // Reverse if needed - if left_startnode == i { - left_route.reverse(); - } - if right_endnode == j { - right_route.reverse(); - } - - // Create new route - let mut new_route = left_route; - new_route.extend(right_route); - - // Update routes and demands - let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); - routes[start] = Some(new_route.clone()); - routes[end] = Some(new_route); - route_demands[start] = merged_demand; - route_demands[end] = merged_demand; - } - - let mut final_routes = Vec::new(); - - for (i, opt_route) in routes.into_iter().enumerate() { - if let Some(mut route) = opt_route { - if route[0] == i { - let mut full_route = Vec::with_capacity(route.len() + 2); - full_route.push(0); - full_route.append(&mut route); - full_route.push(0); - final_routes.push(full_route); - } - } - } - - Ok(Some(Solution { routes: final_routes })) - -} -#[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/vehicle_routing/cw_heuristic/benchmarker_outbound.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/benchmarker_outbound.rs deleted file mode 100644 index 5ddaecf..0000000 --- a/tig-algorithms/src/vehicle_routing/cw_heuristic/benchmarker_outbound.rs +++ /dev/null @@ -1,133 +0,0 @@ -/*! -Copyright 2024 Just - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - let routes = routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(); - - Ok(Some(Solution { - routes - })) -} - -#[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/vehicle_routing/cw_heuristic/commercial.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/commercial.rs deleted file mode 100644 index 334851d..0000000 --- a/tig-algorithms/src/vehicle_routing/cw_heuristic/commercial.rs +++ /dev/null @@ -1,133 +0,0 @@ -/*! -Copyright 2024 Just - -Licensed under the TIG Commercial License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - let routes = routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(); - - Ok(Some(Solution { - routes - })) -} - -#[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/vehicle_routing/cw_heuristic/inbound.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/inbound.rs deleted file mode 100644 index 7dc1630..0000000 --- a/tig-algorithms/src/vehicle_routing/cw_heuristic/inbound.rs +++ /dev/null @@ -1,133 +0,0 @@ -/*! -Copyright 2024 Just - -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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - let routes = routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(); - - Ok(Some(Solution { - routes - })) -} - -#[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/vehicle_routing/cw_heuristic/innovator_outbound.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/innovator_outbound.rs deleted file mode 100644 index 3087299..0000000 --- a/tig-algorithms/src/vehicle_routing/cw_heuristic/innovator_outbound.rs +++ /dev/null @@ -1,133 +0,0 @@ -/*! -Copyright 2024 Just - -Licensed under the TIG Innovator Outbound Game License v1.0 (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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - let routes = routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(); - - Ok(Some(Solution { - routes - })) -} - -#[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/vehicle_routing/cw_heuristic/mod.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vehicle_routing/cw_heuristic/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/open_data.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/open_data.rs deleted file mode 100644 index d2545b8..0000000 --- a/tig-algorithms/src/vehicle_routing/cw_heuristic/open_data.rs +++ /dev/null @@ -1,133 +0,0 @@ -/*! -Copyright 2024 Just - -Licensed under the TIG Open Data 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 tig_challenges::vehicle_routing::*; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let d = &challenge.distance_matrix; - let c = challenge.max_capacity; - let n = challenge.difficulty.num_nodes; - - let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; - let p = challenge.max_total_distance as f32 / max_dist; - if p < 0.57 { - return Ok(None) - } - - // Clarke-Wright heuristic for node pairs based on their distances to depot - // vs distance between each other - let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); - for i in 1..n { - for j in (i + 1)..n { - scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); - } - } - - scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); - - // Create a route for every node - let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); - routes[0] = None; - let mut route_demands: Vec = challenge.demands.clone(); - - // Iterate through node pairs, starting from greatest score - for (s, i, j) in scores { - // Stop if score is negative - if s < 0 { - break; - } - - // Skip if joining the nodes is not possible - if routes[i].is_none() || routes[j].is_none() { - continue; - } - - let left_route = routes[i].as_ref().unwrap(); - let right_route = routes[j].as_ref().unwrap(); - let mut left_startnode = left_route[0]; - let right_startnode = right_route[0]; - let left_endnode = left_route[left_route.len() - 1]; - let mut right_endnode = right_route[right_route.len() - 1]; - let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; - - if left_startnode == right_startnode || merged_demand > c { - continue; - } - - let mut left_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - routes[left_startnode] = None; - routes[right_startnode] = None; - routes[left_endnode] = None; - routes[right_endnode] = None; - - // reverse it - if left_startnode == i { - left_route.reverse(); - left_startnode = left_endnode; - } - if right_endnode == j { - right_route.reverse(); - right_endnode = right_startnode; - } - - let mut new_route = left_route; - new_route.extend(right_route); - - // Only the start and end nodes of routes are kept - routes[left_startnode] = Some(new_route.clone()); - routes[right_endnode] = Some(new_route); - route_demands[left_startnode] = merged_demand; - route_demands[right_endnode] = merged_demand; - } - - let routes = routes - .into_iter() - .enumerate() - .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) - .map(|(_, mut x)| { - let mut route = vec![0]; - route.append(x.as_mut().unwrap()); - route.push(0); - route - }) - .collect(); - - Ok(Some(Solution { - routes - })) -} - -#[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/vehicle_routing/enhanced_routing/benchmarker_outbound.rs b/tig-algorithms/src/vehicle_routing/enhanced_routing/benchmarker_outbound.rs deleted file mode 100644 index fa025be..0000000 --- a/tig-algorithms/src/vehicle_routing/enhanced_routing/benchmarker_outbound.rs +++ /dev/null @@ -1,328 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Benchmarker Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - if let Some(best_sol) = &best_solution { - let mut solution = Solution { - routes: best_sol.routes.clone() - }; - if try_inter_route_swap(&mut solution, &challenge.distance_matrix, &challenge.demands, challenge.max_capacity) { - let new_cost = calculate_solution_cost(&solution, &challenge.distance_matrix); - if new_cost < best_cost { - best_solution = Some(solution); - } - } - } - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - - let max_distance = challenge.distance_matrix.iter().flat_map(|row| row.iter()).cloned().max().unwrap_or(0); - let threshold = max_distance / 2; - - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - if challenge.distance_matrix[i][j] <= threshold { - savings.push((0.0, i as u8, j as u8)); - } - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - - -#[inline] -fn try_inter_route_swap( - solution: &mut Solution, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> bool { - let mut improved = false; - let num_routes = solution.routes.len(); - - for i in 0..num_routes { - for j in i + 1..num_routes { - if let Some(better_routes) = find_best_swap( - &solution.routes[i], - &solution.routes[j], - distance_matrix, - demands, - max_capacity - ) { - solution.routes[i] = better_routes.0; - solution.routes[j] = better_routes.1; - improved = true; - } - } - } - - improved -} - -#[inline] -fn find_best_swap( - route1: &Vec, - route2: &Vec, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> Option<(Vec, Vec)> { - let mut best_improvement = 0; - let mut best_swap = None; - - for i in 1..route1.len() - 1 { - for j in 1..route2.len() - 1 { - let route1_demand: i32 = route1.iter().map(|&n| demands[n]).sum(); - let route2_demand: i32 = route2.iter().map(|&n| demands[n]).sum(); - let demand_delta = demands[route2[j]] - demands[route1[i]]; - - if route1_demand + demand_delta > max_capacity || - route2_demand - demand_delta > max_capacity { - continue; - } - - let old_cost = distance_matrix[route1[i-1]][route1[i]] + - distance_matrix[route1[i]][route1[i+1]] + - distance_matrix[route2[j-1]][route2[j]] + - distance_matrix[route2[j]][route2[j+1]]; - - let new_cost = distance_matrix[route1[i-1]][route2[j]] + - distance_matrix[route2[j]][route1[i+1]] + - distance_matrix[route2[j-1]][route1[i]] + - distance_matrix[route1[i]][route2[j+1]]; - - let improvement = old_cost - new_cost; - if improvement > best_improvement { - best_improvement = improvement; - let mut new_route1 = route1.clone(); - let mut new_route2 = route2.clone(); - new_route1[i] = route2[j]; - new_route2[j] = route1[i]; - best_swap = Some((new_route1, new_route2)); - } - } - } - - best_swap -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/enhanced_routing/commercial.rs b/tig-algorithms/src/vehicle_routing/enhanced_routing/commercial.rs deleted file mode 100644 index 56a44bf..0000000 --- a/tig-algorithms/src/vehicle_routing/enhanced_routing/commercial.rs +++ /dev/null @@ -1,328 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Commercial License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - if let Some(best_sol) = &best_solution { - let mut solution = Solution { - routes: best_sol.routes.clone() - }; - if try_inter_route_swap(&mut solution, &challenge.distance_matrix, &challenge.demands, challenge.max_capacity) { - let new_cost = calculate_solution_cost(&solution, &challenge.distance_matrix); - if new_cost < best_cost { - best_solution = Some(solution); - } - } - } - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - - let max_distance = challenge.distance_matrix.iter().flat_map(|row| row.iter()).cloned().max().unwrap_or(0); - let threshold = max_distance / 2; - - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - if challenge.distance_matrix[i][j] <= threshold { - savings.push((0.0, i as u8, j as u8)); - } - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - - -#[inline] -fn try_inter_route_swap( - solution: &mut Solution, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> bool { - let mut improved = false; - let num_routes = solution.routes.len(); - - for i in 0..num_routes { - for j in i + 1..num_routes { - if let Some(better_routes) = find_best_swap( - &solution.routes[i], - &solution.routes[j], - distance_matrix, - demands, - max_capacity - ) { - solution.routes[i] = better_routes.0; - solution.routes[j] = better_routes.1; - improved = true; - } - } - } - - improved -} - -#[inline] -fn find_best_swap( - route1: &Vec, - route2: &Vec, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> Option<(Vec, Vec)> { - let mut best_improvement = 0; - let mut best_swap = None; - - for i in 1..route1.len() - 1 { - for j in 1..route2.len() - 1 { - let route1_demand: i32 = route1.iter().map(|&n| demands[n]).sum(); - let route2_demand: i32 = route2.iter().map(|&n| demands[n]).sum(); - let demand_delta = demands[route2[j]] - demands[route1[i]]; - - if route1_demand + demand_delta > max_capacity || - route2_demand - demand_delta > max_capacity { - continue; - } - - let old_cost = distance_matrix[route1[i-1]][route1[i]] + - distance_matrix[route1[i]][route1[i+1]] + - distance_matrix[route2[j-1]][route2[j]] + - distance_matrix[route2[j]][route2[j+1]]; - - let new_cost = distance_matrix[route1[i-1]][route2[j]] + - distance_matrix[route2[j]][route1[i+1]] + - distance_matrix[route2[j-1]][route1[i]] + - distance_matrix[route1[i]][route2[j+1]]; - - let improvement = old_cost - new_cost; - if improvement > best_improvement { - best_improvement = improvement; - let mut new_route1 = route1.clone(); - let mut new_route2 = route2.clone(); - new_route1[i] = route2[j]; - new_route2[j] = route1[i]; - best_swap = Some((new_route1, new_route2)); - } - } - } - - best_swap -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/enhanced_routing/inbound.rs b/tig-algorithms/src/vehicle_routing/enhanced_routing/inbound.rs deleted file mode 100644 index ed90db1..0000000 --- a/tig-algorithms/src/vehicle_routing/enhanced_routing/inbound.rs +++ /dev/null @@ -1,328 +0,0 @@ -/*! -Copyright 2024 syebastian - -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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - if let Some(best_sol) = &best_solution { - let mut solution = Solution { - routes: best_sol.routes.clone() - }; - if try_inter_route_swap(&mut solution, &challenge.distance_matrix, &challenge.demands, challenge.max_capacity) { - let new_cost = calculate_solution_cost(&solution, &challenge.distance_matrix); - if new_cost < best_cost { - best_solution = Some(solution); - } - } - } - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - - let max_distance = challenge.distance_matrix.iter().flat_map(|row| row.iter()).cloned().max().unwrap_or(0); - let threshold = max_distance / 2; - - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - if challenge.distance_matrix[i][j] <= threshold { - savings.push((0.0, i as u8, j as u8)); - } - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - - -#[inline] -fn try_inter_route_swap( - solution: &mut Solution, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> bool { - let mut improved = false; - let num_routes = solution.routes.len(); - - for i in 0..num_routes { - for j in i + 1..num_routes { - if let Some(better_routes) = find_best_swap( - &solution.routes[i], - &solution.routes[j], - distance_matrix, - demands, - max_capacity - ) { - solution.routes[i] = better_routes.0; - solution.routes[j] = better_routes.1; - improved = true; - } - } - } - - improved -} - -#[inline] -fn find_best_swap( - route1: &Vec, - route2: &Vec, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> Option<(Vec, Vec)> { - let mut best_improvement = 0; - let mut best_swap = None; - - for i in 1..route1.len() - 1 { - for j in 1..route2.len() - 1 { - let route1_demand: i32 = route1.iter().map(|&n| demands[n]).sum(); - let route2_demand: i32 = route2.iter().map(|&n| demands[n]).sum(); - let demand_delta = demands[route2[j]] - demands[route1[i]]; - - if route1_demand + demand_delta > max_capacity || - route2_demand - demand_delta > max_capacity { - continue; - } - - let old_cost = distance_matrix[route1[i-1]][route1[i]] + - distance_matrix[route1[i]][route1[i+1]] + - distance_matrix[route2[j-1]][route2[j]] + - distance_matrix[route2[j]][route2[j+1]]; - - let new_cost = distance_matrix[route1[i-1]][route2[j]] + - distance_matrix[route2[j]][route1[i+1]] + - distance_matrix[route2[j-1]][route1[i]] + - distance_matrix[route1[i]][route2[j+1]]; - - let improvement = old_cost - new_cost; - if improvement > best_improvement { - best_improvement = improvement; - let mut new_route1 = route1.clone(); - let mut new_route2 = route2.clone(); - new_route1[i] = route2[j]; - new_route2[j] = route1[i]; - best_swap = Some((new_route1, new_route2)); - } - } - } - - best_swap -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/enhanced_routing/innovator_outbound.rs b/tig-algorithms/src/vehicle_routing/enhanced_routing/innovator_outbound.rs deleted file mode 100644 index 50327a4..0000000 --- a/tig-algorithms/src/vehicle_routing/enhanced_routing/innovator_outbound.rs +++ /dev/null @@ -1,328 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Innovator Outbound Game License v1.0 (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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - if let Some(best_sol) = &best_solution { - let mut solution = Solution { - routes: best_sol.routes.clone() - }; - if try_inter_route_swap(&mut solution, &challenge.distance_matrix, &challenge.demands, challenge.max_capacity) { - let new_cost = calculate_solution_cost(&solution, &challenge.distance_matrix); - if new_cost < best_cost { - best_solution = Some(solution); - } - } - } - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - - let max_distance = challenge.distance_matrix.iter().flat_map(|row| row.iter()).cloned().max().unwrap_or(0); - let threshold = max_distance / 2; - - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - if challenge.distance_matrix[i][j] <= threshold { - savings.push((0.0, i as u8, j as u8)); - } - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - - -#[inline] -fn try_inter_route_swap( - solution: &mut Solution, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> bool { - let mut improved = false; - let num_routes = solution.routes.len(); - - for i in 0..num_routes { - for j in i + 1..num_routes { - if let Some(better_routes) = find_best_swap( - &solution.routes[i], - &solution.routes[j], - distance_matrix, - demands, - max_capacity - ) { - solution.routes[i] = better_routes.0; - solution.routes[j] = better_routes.1; - improved = true; - } - } - } - - improved -} - -#[inline] -fn find_best_swap( - route1: &Vec, - route2: &Vec, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> Option<(Vec, Vec)> { - let mut best_improvement = 0; - let mut best_swap = None; - - for i in 1..route1.len() - 1 { - for j in 1..route2.len() - 1 { - let route1_demand: i32 = route1.iter().map(|&n| demands[n]).sum(); - let route2_demand: i32 = route2.iter().map(|&n| demands[n]).sum(); - let demand_delta = demands[route2[j]] - demands[route1[i]]; - - if route1_demand + demand_delta > max_capacity || - route2_demand - demand_delta > max_capacity { - continue; - } - - let old_cost = distance_matrix[route1[i-1]][route1[i]] + - distance_matrix[route1[i]][route1[i+1]] + - distance_matrix[route2[j-1]][route2[j]] + - distance_matrix[route2[j]][route2[j+1]]; - - let new_cost = distance_matrix[route1[i-1]][route2[j]] + - distance_matrix[route2[j]][route1[i+1]] + - distance_matrix[route2[j-1]][route1[i]] + - distance_matrix[route1[i]][route2[j+1]]; - - let improvement = old_cost - new_cost; - if improvement > best_improvement { - best_improvement = improvement; - let mut new_route1 = route1.clone(); - let mut new_route2 = route2.clone(); - new_route1[i] = route2[j]; - new_route2[j] = route1[i]; - best_swap = Some((new_route1, new_route2)); - } - } - } - - best_swap -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/enhanced_routing/mod.rs b/tig-algorithms/src/vehicle_routing/enhanced_routing/mod.rs deleted file mode 100644 index fcec967..0000000 --- a/tig-algorithms/src/vehicle_routing/enhanced_routing/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod benchmarker_outbound; -pub use benchmarker_outbound::solve_challenge; -#[cfg(feature = "cuda")] -pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/enhanced_routing/open_data.rs b/tig-algorithms/src/vehicle_routing/enhanced_routing/open_data.rs deleted file mode 100644 index 22705ca..0000000 --- a/tig-algorithms/src/vehicle_routing/enhanced_routing/open_data.rs +++ /dev/null @@ -1,328 +0,0 @@ -/*! -Copyright 2024 syebastian - -Licensed under the TIG Open Data 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::{SmallRng, StdRng}, Rng, SeedableRng}; -use tig_challenges::vehicle_routing::{Challenge, Solution}; - -pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut best_solution: Option = None; - let mut best_cost = std::i32::MAX; - - const INITIAL_TEMPERATURE: f32 = 2.0; - const COOLING_RATE: f32 = 0.995; - const ITERATIONS_PER_TEMPERATURE: usize = 2; - - let num_nodes = challenge.difficulty.num_nodes; - - let mut current_params = vec![1.0; num_nodes]; - let mut savings_list = create_initial_savings_list(challenge); - recompute_and_sort_savings(&mut savings_list, ¤t_params, challenge); - - let mut current_solution = create_solution(challenge, ¤t_params, &savings_list); - let mut current_cost = calculate_solution_cost(¤t_solution, &challenge.distance_matrix); - - if current_cost <= challenge.max_total_distance { - return Ok(Some(current_solution)); - } - - if (current_cost as f32 * 0.96) > challenge.max_total_distance as f32 { - return Ok(None); - } - - let mut temperature = INITIAL_TEMPERATURE; - let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap())); - - while temperature > 1.0 { - for _ in 0..ITERATIONS_PER_TEMPERATURE { - let neighbor_params = generate_neighbor(¤t_params, &mut rng); - recompute_and_sort_savings(&mut savings_list, &neighbor_params, challenge); - let mut neighbor_solution = create_solution(challenge, &neighbor_params, &savings_list); - apply_local_search_until_no_improvement(&mut neighbor_solution, &challenge.distance_matrix); - let neighbor_cost = calculate_solution_cost(&neighbor_solution, &challenge.distance_matrix); - - let delta = neighbor_cost as f32 - current_cost as f32; - if delta < 0.0 || rng.gen::() < (-delta / temperature).exp() { - current_params = neighbor_params; - current_cost = neighbor_cost; - current_solution = neighbor_solution; - - if current_cost < best_cost { - best_cost = current_cost; - best_solution = Some(Solution { - routes: current_solution.routes.clone(), - }); - } - } - if best_cost <= challenge.max_total_distance { - return Ok(best_solution); - } - } - - temperature *= COOLING_RATE; - } - - if let Some(best_sol) = &best_solution { - let mut solution = Solution { - routes: best_sol.routes.clone() - }; - if try_inter_route_swap(&mut solution, &challenge.distance_matrix, &challenge.demands, challenge.max_capacity) { - let new_cost = calculate_solution_cost(&solution, &challenge.distance_matrix); - if new_cost < best_cost { - best_solution = Some(solution); - } - } - } - Ok(best_solution) -} - -#[inline] -fn create_initial_savings_list(challenge: &Challenge) -> Vec<(f32, u8, u8)> { - let num_nodes = challenge.difficulty.num_nodes; - let capacity = ((num_nodes - 1) * (num_nodes - 2)) / 2; - let mut savings = Vec::with_capacity(capacity); - - let max_distance = challenge.distance_matrix.iter().flat_map(|row| row.iter()).cloned().max().unwrap_or(0); - let threshold = max_distance / 2; - - for i in 1..num_nodes { - for j in (i + 1)..num_nodes { - if challenge.distance_matrix[i][j] <= threshold { - savings.push((0.0, i as u8, j as u8)); - } - } - } - savings -} - -#[inline] -fn recompute_and_sort_savings(savings_list: &mut [(f32, u8, u8)], params: &[f32], challenge: &Challenge) { - let distance_matrix = &challenge.distance_matrix; - - let mut zero_len = 0; - for (score, i, j) in savings_list.iter_mut() { - let i = *i as usize; - let j = *j as usize; - *score = params[i] * distance_matrix[0][i] as f32 + - params[j] * distance_matrix[j][0] as f32 - - params[i] * params[j] * distance_matrix[i][j] as f32; - } - - savings_list.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); -} - -#[inline] -fn generate_neighbor(current: &[f32], rng: &mut R) -> Vec { - current.iter().map(|¶m| { - let delta = rng.gen_range(-0.1..=0.1); - (param + delta).clamp(0.0, 2.0) - }).collect() -} - -#[inline] -fn apply_local_search_until_no_improvement(solution: &mut Solution, distance_matrix: &Vec>) { - let mut improved = true; - while improved { - improved = false; - for route in &mut solution.routes { - if two_opt(route, distance_matrix) { - improved = true; - } - } - } -} -#[inline] -fn two_opt(route: &mut Vec, distance_matrix: &Vec>) -> bool { - let n = route.len(); - let mut improved = false; - - for i in 1..n - 2 { - for j in i + 1..n - 1 { - let current_distance = distance_matrix[route[i - 1]][route[i]] - + distance_matrix[route[j]][route[j + 1]]; - let new_distance = distance_matrix[route[i - 1]][route[j]] - + distance_matrix[route[i]][route[j + 1]]; - - if new_distance < current_distance { - route[i..=j].reverse(); - improved = true; - } - } - } - - improved -} - -#[inline] -fn calculate_solution_cost(solution: &Solution, distance_matrix: &Vec>) -> i32 { - solution.routes.iter().map(|route| { - route.windows(2).map(|w| distance_matrix[w[0]][w[1]]).sum::() - }).sum() -} - -#[inline] -fn create_solution(challenge: &Challenge, params: &[f32], savings_list: &[(f32, u8, u8)]) -> Solution { - let distance_matrix = &challenge.distance_matrix; - let max_capacity = challenge.max_capacity; - let num_nodes = challenge.difficulty.num_nodes; - let demands = &challenge.demands; - - let mut routes = vec![None; num_nodes]; - for i in 1..num_nodes { - routes[i] = Some(vec![i]); - } - let mut route_demands = demands.clone(); - - for &(_, i, j) in savings_list { - let (i, j) = (i as usize, j as usize); - if let (Some(left_route), Some(right_route)) = (routes[i].as_ref(), routes[j].as_ref()) { - let (left_start, left_end) = (*left_route.first().unwrap(), *left_route.last().unwrap()); - let (right_start, right_end) = (*right_route.first().unwrap(), *right_route.last().unwrap()); - - if left_start == right_start || route_demands[left_start] + route_demands[right_start] > max_capacity { - continue; - } - - let mut new_route = routes[i].take().unwrap(); - let mut right_route = routes[j].take().unwrap(); - - if left_start == i { new_route.reverse(); } - if right_end == j { right_route.reverse(); } - - new_route.extend(right_route); - - let combined_demand = route_demands[left_start] + route_demands[right_start]; - let new_start = new_route[0]; - let new_end = *new_route.last().unwrap(); - - route_demands[new_start] = combined_demand; - route_demands[new_end] = combined_demand; - - routes[new_start] = Some(new_route.clone()); - routes[new_end] = Some(new_route); - } - } - - Solution { - routes: routes - .into_iter() - .enumerate() - .filter_map(|(i, route)| route.filter(|r| r[0] == i)) - .map(|mut route| { - route.insert(0, 0); - route.push(0); - route - }) - .collect(), - } -} - - -#[inline] -fn try_inter_route_swap( - solution: &mut Solution, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> bool { - let mut improved = false; - let num_routes = solution.routes.len(); - - for i in 0..num_routes { - for j in i + 1..num_routes { - if let Some(better_routes) = find_best_swap( - &solution.routes[i], - &solution.routes[j], - distance_matrix, - demands, - max_capacity - ) { - solution.routes[i] = better_routes.0; - solution.routes[j] = better_routes.1; - improved = true; - } - } - } - - improved -} - -#[inline] -fn find_best_swap( - route1: &Vec, - route2: &Vec, - distance_matrix: &Vec>, - demands: &Vec, - max_capacity: i32 -) -> Option<(Vec, Vec)> { - let mut best_improvement = 0; - let mut best_swap = None; - - for i in 1..route1.len() - 1 { - for j in 1..route2.len() - 1 { - let route1_demand: i32 = route1.iter().map(|&n| demands[n]).sum(); - let route2_demand: i32 = route2.iter().map(|&n| demands[n]).sum(); - let demand_delta = demands[route2[j]] - demands[route1[i]]; - - if route1_demand + demand_delta > max_capacity || - route2_demand - demand_delta > max_capacity { - continue; - } - - let old_cost = distance_matrix[route1[i-1]][route1[i]] + - distance_matrix[route1[i]][route1[i+1]] + - distance_matrix[route2[j-1]][route2[j]] + - distance_matrix[route2[j]][route2[j+1]]; - - let new_cost = distance_matrix[route1[i-1]][route2[j]] + - distance_matrix[route2[j]][route1[i+1]] + - distance_matrix[route2[j-1]][route1[i]] + - distance_matrix[route1[i]][route2[j+1]]; - - let improvement = old_cost - new_cost; - if improvement > best_improvement { - best_improvement = improvement; - let mut new_route1 = route1.clone(); - let mut new_route2 = route2.clone(); - new_route1[i] = route2[j]; - new_route2[j] = route1[i]; - best_swap = Some((new_route1, new_route2)); - } - } - } - - best_swap -} - -#[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}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/mod.rs b/tig-algorithms/src/vehicle_routing/mod.rs index 0249d21..930b4fb 100644 --- a/tig-algorithms/src/vehicle_routing/mod.rs +++ b/tig-algorithms/src/vehicle_routing/mod.rs @@ -1,5 +1,4 @@ -pub mod clarke_wright; -pub use clarke_wright as c002_a001; +// c002_a001 // c002_a002 @@ -67,11 +66,9 @@ pub use clarke_wright as c002_a001; // c002_a034 -pub mod cw_heuristic; -pub use cw_heuristic as c002_a035; +// c002_a035 -pub mod clarke_wright_super; -pub use clarke_wright_super as c002_a036; +// c002_a036 // c002_a037 @@ -97,8 +94,7 @@ pub use clarke_wright_super as c002_a036; // c002_a048 -pub mod advanced_routing; -pub use advanced_routing as c002_a049; +// c002_a049 // c002_a050 @@ -106,11 +102,9 @@ pub use advanced_routing as c002_a049; // c002_a052 -pub mod enhanced_routing; -pub use enhanced_routing as c002_a053; +// c002_a053 -pub mod advanced_heuristics; -pub use advanced_heuristics as c002_a054; +// c002_a054 // c002_a055 diff --git a/tig-algorithms/src/vehicle_routing/template.rs b/tig-algorithms/src/vehicle_routing/template.rs index b5c872b..02a7cab 100644 --- a/tig-algorithms/src/vehicle_routing/template.rs +++ b/tig-algorithms/src/vehicle_routing/template.rs @@ -1,7 +1,11 @@ /*! -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.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 @@ -13,6 +17,24 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the speci language governing permissions and limitations under the License. */ +// REMOVE BELOW SECTION IF UNUSED +/* +REFERENCES AND ACKNOWLEDGMENTS + +This implementation is based on or inspired by existing work. Citations and +acknowledgments below: + +1. Academic Papers: + - [Author(s), "Paper Title", DOI (if available)] + +2. Code References: + - [Author(s), URL] + +3. Other: + - [Author(s), Details] + +*/ + // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge use anyhow::{anyhow, Result}; use tig_challenges::vehicle_routing::{Challenge, Solution}; diff --git a/tig-algorithms/wasm/knapsack/classic_quadkp.wasm b/tig-algorithms/wasm/knapsack/classic_quadkp.wasm deleted file mode 100644 index 8fcd958..0000000 Binary files a/tig-algorithms/wasm/knapsack/classic_quadkp.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/knapsack/dynamic.wasm b/tig-algorithms/wasm/knapsack/dynamic.wasm deleted file mode 100644 index f40eead..0000000 Binary files a/tig-algorithms/wasm/knapsack/dynamic.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/knapsack/knapheudp.wasm b/tig-algorithms/wasm/knapsack/knapheudp.wasm deleted file mode 100644 index d918cce..0000000 Binary files a/tig-algorithms/wasm/knapsack/knapheudp.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/knapsack/knapmaxxing.wasm b/tig-algorithms/wasm/knapsack/knapmaxxing.wasm deleted file mode 100644 index b638fd0..0000000 Binary files a/tig-algorithms/wasm/knapsack/knapmaxxing.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/knapsack/quadkp_improved.wasm b/tig-algorithms/wasm/knapsack/quadkp_improved.wasm deleted file mode 100644 index d3790ec..0000000 Binary files a/tig-algorithms/wasm/knapsack/quadkp_improved.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/fast_walk_sat.wasm b/tig-algorithms/wasm/satisfiability/fast_walk_sat.wasm deleted file mode 100644 index 55b1a25..0000000 Binary files a/tig-algorithms/wasm/satisfiability/fast_walk_sat.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/inbound.wasm b/tig-algorithms/wasm/satisfiability/inbound.wasm deleted file mode 100644 index ca5eb20..0000000 Binary files a/tig-algorithms/wasm/satisfiability/inbound.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/sat_adaptive.wasm b/tig-algorithms/wasm/satisfiability/sat_adaptive.wasm deleted file mode 100644 index 98eae0e..0000000 Binary files a/tig-algorithms/wasm/satisfiability/sat_adaptive.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/sat_allocd.wasm b/tig-algorithms/wasm/satisfiability/sat_allocd.wasm deleted file mode 100644 index 38f9984..0000000 Binary files a/tig-algorithms/wasm/satisfiability/sat_allocd.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/sat_global.wasm b/tig-algorithms/wasm/satisfiability/sat_global.wasm deleted file mode 100644 index a36133b..0000000 Binary files a/tig-algorithms/wasm/satisfiability/sat_global.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/sat_global_opt.wasm b/tig-algorithms/wasm/satisfiability/sat_global_opt.wasm deleted file mode 100644 index 94b46b5..0000000 Binary files a/tig-algorithms/wasm/satisfiability/sat_global_opt.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/sat_optima.wasm b/tig-algorithms/wasm/satisfiability/sat_optima.wasm deleted file mode 100644 index 22350fe..0000000 Binary files a/tig-algorithms/wasm/satisfiability/sat_optima.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/schnoing.wasm b/tig-algorithms/wasm/satisfiability/schnoing.wasm deleted file mode 100644 index 4d57267..0000000 Binary files a/tig-algorithms/wasm/satisfiability/schnoing.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/sprint_sat.wasm b/tig-algorithms/wasm/satisfiability/sprint_sat.wasm deleted file mode 100644 index 38fb4ad..0000000 Binary files a/tig-algorithms/wasm/satisfiability/sprint_sat.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/satisfiability/walk_sat.wasm b/tig-algorithms/wasm/satisfiability/walk_sat.wasm deleted file mode 100644 index e0a7d8d..0000000 Binary files a/tig-algorithms/wasm/satisfiability/walk_sat.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vector_search/brute_force_bacalhau.wasm b/tig-algorithms/wasm/vector_search/brute_force_bacalhau.wasm deleted file mode 100644 index 9c34e15..0000000 Binary files a/tig-algorithms/wasm/vector_search/brute_force_bacalhau.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vector_search/invector.wasm b/tig-algorithms/wasm/vector_search/invector.wasm deleted file mode 100644 index 87fc64c..0000000 Binary files a/tig-algorithms/wasm/vector_search/invector.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vector_search/invector_hybrid.wasm b/tig-algorithms/wasm/vector_search/invector_hybrid.wasm deleted file mode 100644 index 2ba2b5f..0000000 Binary files a/tig-algorithms/wasm/vector_search/invector_hybrid.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vector_search/optimax_gpu.wasm b/tig-algorithms/wasm/vector_search/optimax_gpu.wasm deleted file mode 100644 index 4653787..0000000 Binary files a/tig-algorithms/wasm/vector_search/optimax_gpu.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vehicle_routing/advanced_heuristics.wasm b/tig-algorithms/wasm/vehicle_routing/advanced_heuristics.wasm deleted file mode 100644 index 1c50528..0000000 Binary files a/tig-algorithms/wasm/vehicle_routing/advanced_heuristics.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vehicle_routing/advanced_routing.wasm b/tig-algorithms/wasm/vehicle_routing/advanced_routing.wasm deleted file mode 100644 index 87cc27b..0000000 Binary files a/tig-algorithms/wasm/vehicle_routing/advanced_routing.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vehicle_routing/clarke_wright.wasm b/tig-algorithms/wasm/vehicle_routing/clarke_wright.wasm deleted file mode 100644 index c778d20..0000000 Binary files a/tig-algorithms/wasm/vehicle_routing/clarke_wright.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vehicle_routing/clarke_wright_super.wasm b/tig-algorithms/wasm/vehicle_routing/clarke_wright_super.wasm deleted file mode 100644 index 929b53d..0000000 Binary files a/tig-algorithms/wasm/vehicle_routing/clarke_wright_super.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vehicle_routing/cw_heuristic.wasm b/tig-algorithms/wasm/vehicle_routing/cw_heuristic.wasm deleted file mode 100644 index 0294ce0..0000000 Binary files a/tig-algorithms/wasm/vehicle_routing/cw_heuristic.wasm and /dev/null differ diff --git a/tig-algorithms/wasm/vehicle_routing/enhanced_routing.wasm b/tig-algorithms/wasm/vehicle_routing/enhanced_routing.wasm deleted file mode 100644 index a8b736e..0000000 Binary files a/tig-algorithms/wasm/vehicle_routing/enhanced_routing.wasm and /dev/null differ diff --git a/tig-benchmarker/calc_apy.py b/tig-benchmarker/calc_apy.py index a1cf1a8..0198354 100644 --- a/tig-benchmarker/calc_apy.py +++ b/tig-benchmarker/calc_apy.py @@ -25,7 +25,10 @@ opow_data = { factors = { benchmarker: { **{ - f: opow_data[benchmarker].block_data.num_qualifiers_by_challenge.get(f, 0) + f: ( + opow_data[benchmarker].block_data.num_qualifiers_by_challenge.get(f, 0) * + opow_data[benchmarker].block_data.solution_ratio_by_challenge.get(f, 0) + ) for f in block.data.active_ids["challenge"] }, "weighted_deposit": opow_data[benchmarker].block_data.delegated_weighted_deposit.to_float() @@ -49,7 +52,7 @@ total_factors = { for f in list(block.data.active_ids["challenge"]) + ["weighted_deposit"] } reward_shares = { - benchmarker: opow_data[benchmarker].block_data.reward_share + benchmarker: opow_data[benchmarker].block_data.reward_share.to_float() / (opow_data[benchmarker].block_data.reward.to_float() + 1e-12) for benchmarker in opow_data } diff --git a/tig-benchmarker/calc_reward_share.py b/tig-benchmarker/calc_reward_share.py index 853053a..650d689 100644 --- a/tig-benchmarker/calc_reward_share.py +++ b/tig-benchmarker/calc_reward_share.py @@ -19,7 +19,10 @@ player_data = Player.from_dict(requests.get(f"{API_URL}/get-player-data?player_i factors = { benchmarker: { **{ - f: opow_data[benchmarker].block_data.num_qualifiers_by_challenge.get(f, 0) + f: ( + opow_data[benchmarker].block_data.num_qualifiers_by_challenge.get(f, 0) * + opow_data[benchmarker].block_data.solution_ratio_by_challenge.get(f, 0) + ) for f in block.data.active_ids["challenge"] }, "weighted_deposit": opow_data[benchmarker].block_data.delegated_weighted_deposit.to_float() diff --git a/tig-benchmarker/check_reliability.py b/tig-benchmarker/check_reliability.py new file mode 100644 index 0000000..4154161 --- /dev/null +++ b/tig-benchmarker/check_reliability.py @@ -0,0 +1,54 @@ +import requests + +API_URL = "https://mainnet-api.tig.foundation" + +print(f"Fetching block, challenges, and benchmarkers..") +block = requests.get(f"{API_URL}/get-block").json()["block"] +challenges = sorted( + requests.get(f"{API_URL}/get-challenges?block_id={block['id']}").json()["challenges"], + key=lambda x: x["id"] +) +opow = requests.get(f"{API_URL}/get-opow?block_id={block['id']}").json()["opow"] + +print("Challenges:") +for i, c in enumerate(challenges): + print(f"{i+1}) {c['details']['name']}") +c = challenges[int(input("Enter challenge index: ")) - 1] +print("") + +for x in opow: + x["reliability"] = x["block_data"]["solution_ratio_by_challenge"].get(c["id"], 0) / c["block_data"]["average_solution_ratio"] + +opow = sorted(opow, key=lambda x: x["reliability"], reverse=True) + +print(f"Benchmarkers Reliability for {c['details']['name']}:") +for i, x in enumerate(opow): + print(f"{i+1}) {x['player_id']}: {x['reliability']:.4f}") + +player = opow[int(input("Enter benchmarker index: ")) - 1] +print("") +print(f"Fetching benchmarks for {player['player_id']}..") +d = requests.get(f"{API_URL}/get-benchmarks?player_id={player['player_id']}&block_id={block['id']}").json() +precommits = {x["benchmark_id"]: x for x in d["precommits"]} +benchmarks = {x["id"]: x for x in d["benchmarks"]} +proofs = {x["benchmark_id"]: x for x in d["proofs"]} +frauds = {x["benchmark_id"]: x for x in d["frauds"]} + +total_solutions = {} +total_nonces = {} +for b_id in proofs: + p = precommits[b_id] + b = benchmarks[b_id] + if b_id in frauds or p["settings"]["challenge_id"] != c["id"]: + continue + k = f"difficulty {p['settings']['difficulty']}, algorithm_id: {p['settings']['algorithm_id']}" + total_solutions[k] = total_solutions.get(k, 0) + b["details"]["num_solutions"] + total_nonces[k] = total_nonces.get(k, 0) + p["details"]["num_nonces"] + +reliability = { + k: total_solutions[k] / total_nonces[k] / c["block_data"]["average_solution_ratio"] + for k in total_solutions +} +reliability = sorted(reliability.items(), key=lambda x: x[1], reverse=True) +for k, r in reliability: + print(f"{k}, num_solutions: {total_solutions[k]}, num_nonces: {total_nonces[k]}, reliability: {r:.4f}") \ No newline at end of file diff --git a/tig-benchmarker/common/structs.py b/tig-benchmarker/common/structs.py index 4a69547..e7146e2 100644 --- a/tig-benchmarker/common/structs.py +++ b/tig-benchmarker/common/structs.py @@ -183,6 +183,7 @@ class ChallengeState(FromDict): class ChallengeBlockData(FromDict): num_qualifiers: int qualifier_difficulties: Set[Point] + average_solution_ratio: float base_frontier: Frontier scaled_frontier: Frontier scaling_factor: float @@ -203,7 +204,9 @@ class OPoWBlockData(FromDict): delegated_weighted_deposit: PreciseNumber self_deposit: PreciseNumber delegators: Set[str] - reward_share: float + coinbase: Dict[str, PreciseNumber] + solution_ratio_by_challenge: Dict[str, float] + reward_share: PreciseNumber imbalance: PreciseNumber influence: PreciseNumber reward: PreciseNumber diff --git a/tig-benchmarker/slave.py b/tig-benchmarker/slave.py index 60c66f5..4f1594b 100644 --- a/tig-benchmarker/slave.py +++ b/tig-benchmarker/slave.py @@ -303,4 +303,4 @@ if __name__ == "__main__": level=logging.DEBUG if args.verbose else logging.INFO ) - main(args.master, args.tig_worker_path, args.download, args.workers, args.name, args.port, args.output, args.ttl) \ No newline at end of file + main(args.master, args.tig_worker_path, args.download, args.workers, args.name, args.port, args.output, args.ttl) diff --git a/tig-benchmarker/slave_try.py b/tig-benchmarker/slave_try.py deleted file mode 100644 index cde0bdd..0000000 --- a/tig-benchmarker/slave_try.py +++ /dev/null @@ -1,156 +0,0 @@ -import argparse -import json -import os -import logging -import randomname -import aiohttp -import asyncio -import subprocess -import time - -logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) - -def now(): - return int(time.time() * 1000) - -async def download_wasm(session, download_url, wasm_path): - if not os.path.exists(wasm_path): - start = now() - logger.info(f"downloading WASM from {download_url}") - async with session.get(download_url) as resp: - if resp.status != 200: - raise Exception(f"status {resp.status} when downloading WASM: {await resp.text()}") - with open(wasm_path, 'wb') as f: - f.write(await resp.read()) - logger.debug(f"downloading WASM: took {now() - start}ms") - logger.debug(f"WASM Path: {wasm_path}") - -async def run_tig_worker(tig_worker_path, batch, wasm_path, num_workers): - start = now() - cmd = [ - tig_worker_path, "compute_batch", - json.dumps(batch["settings"]), - batch["rand_hash"], - str(batch["start_nonce"]), - str(batch["num_nonces"]), - str(batch["batch_size"]), - wasm_path, - "--mem", str(batch["runtime_config"]["max_memory"]), - "--fuel", str(batch["runtime_config"]["max_fuel"]), - "--workers", str(num_workers), - ] - if batch["sampled_nonces"]: - cmd += ["--sampled", *map(str, batch["sampled_nonces"])] - logger.info(f"computing batch: {' '.join(cmd)}") - process = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - if process.returncode != 0: - raise Exception(f"tig-worker failed: {stderr.decode()}") - result = json.loads(stdout.decode()) - logger.info(f"computing batch took {now() - start}ms") - logger.debug(f"batch result: {result}") - return result - -async def process_batch(session, master_ip, master_port, tig_worker_path, download_wasms_folder, num_workers, batch, headers): - try: - batch_id = f"{batch['benchmark_id']}_{batch['start_nonce']}" - logger.info(f"Processing batch {batch_id}: {batch}") - - # Step 2: Download WASM - wasm_path = os.path.join(download_wasms_folder, f"{batch['settings']['algorithm_id']}.wasm") - await download_wasm(session, batch['download_url'], wasm_path) - - # Step 3: Run tig-worker - result = await run_tig_worker(tig_worker_path, batch, wasm_path, num_workers) - - # Step 4: Submit results - start = now() - submit_url = f"http://{master_ip}:{master_port}/submit-batch-result/{batch_id}" - logger.info(f"posting results to {submit_url}") - async with session.post(submit_url, json=result, headers=headers) as resp: - if resp.status != 200: - raise Exception(f"status {resp.status} when posting results to master: {await resp.text()}") - logger.debug(f"posting results took {now() - start} ms") - - except Exception as e: - logger.error(f"Error processing batch {batch_id}: {e}") - -async def main( - master_ip: str, - tig_worker_path: str, - download_wasms_folder: str, - num_workers: int, - slave_name: str, - master_port: int -): - if not os.path.exists(tig_worker_path): - raise FileNotFoundError(f"tig-worker not found at path: {tig_worker_path}") - os.makedirs(download_wasms_folder, exist_ok=True) - - headers = { - "User-Agent": slave_name - } - - - async with aiohttp.ClientSession() as session: - while True: - try: - # Step 1: Query for job test maj - start = now() - get_batch_url = f"http://{master_ip}:{master_port}/get-batches" - logger.info(f"fetching job from {get_batch_url}") - try: - resp = await asyncio.wait_for(session.get(get_batch_url, headers=headers), timeout=5) - if resp.status != 200: - text = await resp.text() - if resp.status == 404 and text.strip() == "No batches available": - # Retry with master_port - 1 - new_port = master_port - 1 - get_batch_url = f"http://{master_ip}:{new_port}/get-batches" - logger.info(f"No batches available on port {master_port}, trying port {new_port}") - resp_retry = await asyncio.wait_for(session.get(get_batch_url, headers=headers), timeout=10) - if resp_retry.status != 200: - raise Exception(f"status {resp_retry.status} when fetching job: {await resp_retry.text()}") - master_port_w = new_port - batches = await resp_retry.json(content_type=None) - else: - raise Exception(f"status {resp.status} when fetching job: {text}") - else: - master_port_w = master_port - batches = await resp.json(content_type=None) - except asyncio.TimeoutError: - logger.error(f"Timeout occurred when fetching job from {get_batch_url}") - continue - logger.debug(f"fetching job: took {now() - start}ms") - - # Process batches concurrently - tasks = [ - process_batch(session, master_ip, master_port_w, tig_worker_path, download_wasms_folder, num_workers, batch, headers) - for batch in batches - ] - await asyncio.gather(*tasks) - - except Exception as e: - logger.error(e) - await asyncio.sleep(2) - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="TIG Slave Benchmarker") - parser.add_argument("master_ip", help="IP address of the master") - parser.add_argument("tig_worker_path", help="Path to tig-worker executable") - parser.add_argument("--download", type=str, default="wasms", help="Folder to download WASMs to (default: wasms)") - parser.add_argument("--workers", type=int, default=8, help="Number of workers (default: 8)") - parser.add_argument("--name", type=str, default=randomname.get_name(), help="Name for the slave (default: randomly generated)") - parser.add_argument("--port", type=int, default=5115, help="Port for master (default: 5115)") - parser.add_argument("--verbose", action='store_true', help="Print debug logs") - - args = parser.parse_args() - - logging.basicConfig( - format='%(levelname)s - [%(name)s] - %(message)s', - level=logging.DEBUG if args.verbose else logging.INFO - ) - - asyncio.run(main(args.master_ip, args.tig_worker_path, args.download, args.workers, args.name, args.port)) diff --git a/tig-challenges/src/knapsack.rs b/tig-challenges/src/knapsack.rs index b098ce4..9e8fe49 100644 --- a/tig-challenges/src/knapsack.rs +++ b/tig-challenges/src/knapsack.rs @@ -35,6 +35,11 @@ impl crate::DifficultyTrait<2> for Difficulty { #[derive(Serialize, Deserialize, Debug)] pub struct Solution { + pub sub_solutions: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SubSolution { pub items: Vec, } @@ -50,19 +55,28 @@ impl TryFrom> for Solution { #[derive(Serialize, Deserialize, Debug)] pub struct Challenge { + pub seed: [u8; 32], + pub difficulty: Difficulty, + pub sub_instances: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SubInstance { pub seed: [u8; 32], pub difficulty: Difficulty, pub weights: Vec, pub values: Vec, pub interaction_values: Vec>, pub max_weight: u32, - pub min_value: u32, + pub baseline_value: u32, } // TIG dev bounty available for a GPU optimisation for instance generation! #[cfg(feature = "cuda")] pub const KERNEL: Option = None; +pub const NUM_SUB_INSTANCES: usize = 16; + impl crate::ChallengeTrait for Challenge { #[cfg(feature = "cuda")] fn cuda_generate_instance( @@ -77,7 +91,53 @@ impl crate::ChallengeTrait for Challenge { fn generate_instance(seed: [u8; 32], difficulty: &Difficulty) -> Result { let mut rng = SmallRng::from_seed(StdRng::from_seed(seed).gen()); + let mut sub_instances = Vec::new(); + for _ in 0..NUM_SUB_INSTANCES { + sub_instances.push(SubInstance::generate_instance(&mut rng, seed, difficulty)?); + } + Ok(Challenge { + seed, + difficulty: difficulty.clone(), + sub_instances, + }) + } + + fn verify_solution(&self, solution: &Solution) -> Result<()> { + let mut better_than_baselines = Vec::new(); + for (i, (sub_instance, sub_solution)) in self + .sub_instances + .iter() + .zip(&solution.sub_solutions) + .enumerate() + { + match sub_instance.verify_solution(&sub_solution) { + Ok(total_value) => better_than_baselines + .push(total_value as f64 / sub_instance.baseline_value as f64 - 1.0), + Err(e) => return Err(anyhow!("Instance {}: {}", i, e.to_string())), + } + } + let average = + better_than_baselines.iter().sum::() / better_than_baselines.len() as f64; + let threshold = self.difficulty.better_than_baseline as f64 / 1000.0; + if average >= threshold { + Ok(()) + } else { + Err(anyhow!( + "Average better_than_baseline ({}) is less than ({})", + average, + threshold + )) + } + } +} + +impl SubInstance { + fn generate_instance( + rng: &mut SmallRng, + seed: [u8; 32], + difficulty: &Difficulty, + ) -> Result { // Set constant density for value generation let density = 0.25; @@ -135,147 +195,29 @@ impl crate::ChallengeTrait for Challenge { // Step 1: Initial solution obtained by greedily selecting items based on value-weight ratio let mut selected_items = Vec::with_capacity(difficulty.num_items); - let mut unselected_items = Vec::with_capacity(difficulty.num_items); let mut total_weight = 0; - let mut total_value = 0; - let mut is_selected = vec![false; difficulty.num_items]; for &(item, _) in &item_values { if total_weight + weights[item] <= max_weight { total_weight += weights[item]; - total_value += values[item] as i32; - - for &prev_item in &selected_items { - total_value += interaction_values[item][prev_item]; - } selected_items.push(item); - is_selected[item] = true; - } else { - unselected_items.push(item); } } - // Step 2: Improvement of solution with Local Search and Tabu-List - // Precompute sum of interaction values with each selected item for all items - let mut interaction_sum_list = vec![0; difficulty.num_items]; - for x in 0..difficulty.num_items { - interaction_sum_list[x] = values[x] as i32; - for &item in &selected_items { - interaction_sum_list[x] += interaction_values[x][item]; - } - } + let baseline_value = calculate_total_value(&selected_items, &values, &interaction_values); - let mut min_selected_item_values = i32::MAX; - for x in 0..difficulty.num_items { - if is_selected[x] { - min_selected_item_values = min_selected_item_values.min(interaction_sum_list[x]); - } - } - - // Optimized local search with tabu list - let max_iterations = 100; - let mut tabu_list = vec![0; difficulty.num_items]; - - for _ in 0..max_iterations { - let mut best_improvement = 0; - let mut best_swap = None; - - for i in 0..unselected_items.len() { - let new_item = unselected_items[i]; - if tabu_list[new_item] > 0 { - continue; - } - - let new_item_values_sum = interaction_sum_list[new_item]; - if new_item_values_sum < best_improvement + min_selected_item_values { - continue; - } - - // Compute minimal weight of remove_item required to put new_item - let min_weight = - weights[new_item] as i32 - (max_weight as i32 - total_weight as i32); - for j in 0..selected_items.len() { - let remove_item = selected_items[j]; - if tabu_list[remove_item] > 0 { - continue; - } - - // Don't check the weight if there is enough remaining capacity - if min_weight > 0 { - // Skip a remove_item if the remaining capacity after removal is insufficient to push a new_item - let removed_item_weight = weights[remove_item] as i32; - if removed_item_weight < min_weight { - continue; - } - } - - let remove_item_values_sum = interaction_sum_list[remove_item]; - let value_diff = new_item_values_sum - - remove_item_values_sum - - interaction_values[new_item][remove_item]; - - if value_diff > best_improvement { - best_improvement = value_diff; - best_swap = Some((i, j)); - } - } - } - - if let Some((unselected_index, selected_index)) = best_swap { - let new_item = unselected_items[unselected_index]; - let remove_item = selected_items[selected_index]; - - selected_items.swap_remove(selected_index); - unselected_items.swap_remove(unselected_index); - selected_items.push(new_item); - unselected_items.push(remove_item); - - is_selected[new_item] = true; - is_selected[remove_item] = false; - - total_value += best_improvement; - total_weight = total_weight + weights[new_item] - weights[remove_item]; - - // Update sum of interaction values after swapping items - min_selected_item_values = i32::MAX; - for x in 0..difficulty.num_items { - interaction_sum_list[x] += - interaction_values[x][new_item] - interaction_values[x][remove_item]; - if is_selected[x] { - min_selected_item_values = - min_selected_item_values.min(interaction_sum_list[x]); - } - } - - // Update tabu list - tabu_list[new_item] = 3; - tabu_list[remove_item] = 3; - } else { - break; // No improvement found, terminate local search - } - - // Decrease tabu counters - for t in tabu_list.iter_mut() { - *t = if *t > 0 { *t - 1 } else { 0 }; - } - } - - let mut min_value = calculate_total_value(&selected_items, &values, &interaction_values); - min_value = (min_value as f32 * (1.0 + difficulty.better_than_baseline as f32 / 1000.0)) - .round() as u32; - - Ok(Challenge { + Ok(SubInstance { seed, difficulty: difficulty.clone(), weights, values, interaction_values, max_weight, - min_value, + baseline_value, }) } - fn verify_solution(&self, solution: &Solution) -> Result<()> { + fn verify_solution(&self, solution: &SubSolution) -> Result { let selected_items: HashSet = solution.items.iter().cloned().collect(); if selected_items.len() != solution.items.len() { return Err(anyhow!("Duplicate items selected.")); @@ -303,14 +245,14 @@ impl crate::ChallengeTrait for Challenge { let selected_items_vec: Vec = selected_items.into_iter().collect(); let total_value = calculate_total_value(&selected_items_vec, &self.values, &self.interaction_values); - if total_value < self.min_value { + if total_value < self.baseline_value { Err(anyhow!( "Total value ({}) does not reach minimum value ({})", total_value, - self.min_value + self.baseline_value )) } else { - Ok(()) + Ok(total_value) } } } diff --git a/tig-challenges/src/vehicle_routing.rs b/tig-challenges/src/vehicle_routing.rs index b478137..b7728b0 100644 --- a/tig-challenges/src/vehicle_routing.rs +++ b/tig-challenges/src/vehicle_routing.rs @@ -34,6 +34,11 @@ impl crate::DifficultyTrait<2> for Difficulty { #[derive(Serialize, Deserialize, Debug)] pub struct Solution { + pub sub_solutions: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SubSolution { pub routes: Vec>, } @@ -49,11 +54,18 @@ impl TryFrom> for Solution { #[derive(Serialize, Deserialize, Debug)] pub struct Challenge { + pub seed: [u8; 32], + pub difficulty: Difficulty, + pub sub_instances: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SubInstance { pub seed: [u8; 32], pub difficulty: Difficulty, pub demands: Vec, pub distance_matrix: Vec>, - pub max_total_distance: i32, + pub baseline_total_distance: i32, pub max_capacity: i32, } @@ -61,6 +73,8 @@ pub struct Challenge { #[cfg(feature = "cuda")] pub const KERNEL: Option = None; +pub const NUM_SUB_INSTANCES: usize = 16; + impl crate::ChallengeTrait for Challenge { #[cfg(feature = "cuda")] fn cuda_generate_instance( @@ -75,7 +89,54 @@ impl crate::ChallengeTrait for Challenge { fn generate_instance(seed: [u8; 32], difficulty: &Difficulty) -> Result { let mut rng = SmallRng::from_seed(StdRng::from_seed(seed).gen()); + let mut sub_instances = Vec::new(); + for _ in 0..NUM_SUB_INSTANCES { + sub_instances.push(SubInstance::generate_instance(&mut rng, seed, difficulty)?); + } + Ok(Challenge { + seed, + difficulty: difficulty.clone(), + sub_instances, + }) + } + + fn verify_solution(&self, solution: &Solution) -> Result<()> { + let mut better_than_baselines = Vec::new(); + for (i, (sub_instance, sub_solution)) in self + .sub_instances + .iter() + .zip(&solution.sub_solutions) + .enumerate() + { + match sub_instance.verify_solution(&sub_solution) { + Ok(total_distance) => better_than_baselines.push( + 1.0 - total_distance as f64 / sub_instance.baseline_total_distance as f64, + ), + Err(e) => return Err(anyhow!("Instance {}: {}", i, e.to_string())), + } + } + let average = + better_than_baselines.iter().sum::() / better_than_baselines.len() as f64; + let threshold = self.difficulty.better_than_baseline as f64 / 1000.0; + if average >= threshold { + Ok(()) + } else { + Err(anyhow!( + "Average better_than_baseline ({}) is less than ({})", + average, + threshold + )) + } + } +} + +impl SubInstance { + fn generate_instance( + rng: &mut SmallRng, + seed: [u8; 32], + difficulty: &Difficulty, + ) -> Result { let num_nodes = difficulty.num_nodes; let max_capacity = 100; @@ -103,28 +164,25 @@ impl crate::ChallengeTrait for Challenge { let baseline_routes = calc_baseline_routes(num_nodes, max_capacity, &demands, &distance_matrix)?; - let baseline_routes_total_distance = calc_routes_total_distance( + let baseline_total_distance = calc_routes_total_distance( num_nodes, max_capacity, &demands, &distance_matrix, &baseline_routes, )?; - let max_total_distance = (baseline_routes_total_distance - * (1000 - difficulty.better_than_baseline as i32) - / 1000) as i32; - Ok(Challenge { + Ok(SubInstance { seed, difficulty: difficulty.clone(), demands, distance_matrix, - max_total_distance, + baseline_total_distance, max_capacity, }) } - fn verify_solution(&self, solution: &Solution) -> Result<()> { + fn verify_solution(&self, solution: &SubSolution) -> Result { let total_distance = calc_routes_total_distance( self.difficulty.num_nodes, self.max_capacity, @@ -132,13 +190,13 @@ impl crate::ChallengeTrait for Challenge { &self.distance_matrix, &solution.routes, )?; - if total_distance <= self.max_total_distance { - Ok(()) + if total_distance <= self.baseline_total_distance { + Ok(total_distance) } else { Err(anyhow!( "Total distance ({}) exceeds max total distance ({})", total_distance, - self.max_total_distance + self.baseline_total_distance )) } } diff --git a/tig-protocol/src/context.rs b/tig-protocol/src/context.rs index 6e89fb4..6f70652 100644 --- a/tig-protocol/src/context.rs +++ b/tig-protocol/src/context.rs @@ -53,6 +53,11 @@ pub trait Context { delegatees: HashMap, ) -> Result<()>; async fn set_player_reward_share(&self, player_id: String, reward_share: f64) -> Result<()>; + async fn set_player_coinbase( + &self, + player_id: String, + coinbase: HashMap, + ) -> Result<()>; async fn set_player_vote( &self, player_id: String, @@ -88,6 +93,7 @@ pub struct AddBlockCache { pub active_players_block_data: HashMap, pub active_opow_block_data: HashMap, pub active_challenges_block_data: HashMap, + pub active_challenges_prev_block_data: HashMap, pub active_algorithms_state: HashMap, pub active_algorithms_details: HashMap, pub active_algorithms_block_data: HashMap, @@ -95,5 +101,5 @@ pub struct AddBlockCache { pub active_breakthroughs_state: HashMap, pub active_breakthroughs_details: HashMap, pub active_breakthroughs_block_data: HashMap, - pub active_solutions: HashMap, + pub active_solutions: Vec<(BenchmarkSettings, u32, u32)>, } diff --git a/tig-protocol/src/contracts/benchmarks.rs b/tig-protocol/src/contracts/benchmarks.rs index 3625fbd..bf86e4d 100644 --- a/tig-protocol/src/contracts/benchmarks.rs +++ b/tig-protocol/src/contracts/benchmarks.rs @@ -18,10 +18,6 @@ pub async fn submit_precommit( return Err(anyhow!("Invalid settings.player_id. Must be {}", player_id)); } - if num_nonces == 0 { - return Err(anyhow!("Invalid num_nonces. Must be greater than 0")); - } - let config = ctx.get_config().await; let latest_block_id = ctx.get_latest_block_id().await; diff --git a/tig-protocol/src/contracts/opow.rs b/tig-protocol/src/contracts/opow.rs index aa00551..b07fd1f 100644 --- a/tig-protocol/src/contracts/opow.rs +++ b/tig-protocol/src/contracts/opow.rs @@ -11,6 +11,7 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { block_details, block_data, active_challenges_block_data, + active_challenges_prev_block_data, active_algorithms_state, active_algorithms_details, active_algorithms_block_data, @@ -53,7 +54,7 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { } let mut num_solutions_by_player_by_challenge = HashMap::>::new(); - for (settings, num_solutions) in active_solutions.values() { + for (settings, num_solutions, _) in active_solutions.iter() { *num_solutions_by_player_by_challenge .entry(settings.player_id.clone()) .or_default() @@ -93,12 +94,13 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { } // update qualifiers - let mut solutions_by_challenge = HashMap::>::new(); - for (settings, num_solutions) in active_solutions.values() { + let mut solutions_by_challenge = + HashMap::>::new(); + for (settings, num_solutions, num_nonces) in active_solutions.iter() { solutions_by_challenge .entry(settings.challenge_id.clone()) .or_default() - .push((settings, num_solutions)); + .push((settings, num_solutions, num_nonces)); } let max_qualifiers_by_player = active_opow_ids @@ -118,7 +120,7 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { let solutions = solutions_by_challenge.get_mut(challenge_id).unwrap(); let points = solutions .iter() - .map(|(settings, _)| settings.difficulty.clone()) + .map(|(settings, _, _)| settings.difficulty.clone()) .collect::(); let mut frontier_indexes = HashMap::::new(); for (frontier_index, frontier) in pareto_algorithm(&points, false).into_iter().enumerate() { @@ -126,60 +128,126 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { frontier_indexes.insert(point, frontier_index); } } - solutions.sort_by(|(a_settings, _), (b_settings, _)| { - let a_index = frontier_indexes[&a_settings.difficulty]; - let b_index = frontier_indexes[&b_settings.difficulty]; - a_index.cmp(&b_index) - }); + let mut solutions_by_frontier_idx = + HashMap::>::new(); + for &x in solutions.iter() { + solutions_by_frontier_idx + .entry(frontier_indexes[&x.0.difficulty]) + .or_default() + .push(x); + } - let mut max_qualifiers_by_player = max_qualifiers_by_player.clone(); - let mut curr_frontier_index = 0; let challenge_data = active_challenges_block_data.get_mut(challenge_id).unwrap(); - for (settings, &num_solutions) in solutions.iter() { - let BenchmarkSettings { - player_id, - algorithm_id, - challenge_id, - difficulty, - .. - } = settings; + let prev_solution_ratio = active_challenges_prev_block_data + .get(challenge_id) + .map(|x| x.average_solution_ratio) + .unwrap_or_default(); + let min_solution_ratio = prev_solution_ratio * config.opow.min_solution_ratio_factor; + let min_num_nonces = config.opow.min_num_nonces as u64; + let mut player_algorithm_solutions = HashMap::>::new(); + let mut player_solutions = HashMap::::new(); + let mut player_nonces = HashMap::::new(); - if curr_frontier_index != frontier_indexes[difficulty] - && challenge_data.num_qualifiers > config.opow.total_qualifiers_threshold + for frontier_idx in 0..solutions_by_frontier_idx.len() { + for (settings, &num_solutions, &num_nonces) in + solutions_by_frontier_idx[&frontier_idx].iter() { + let BenchmarkSettings { + player_id, + algorithm_id, + challenge_id, + difficulty, + .. + } = settings; + + let difficulty_parameters = &config.challenges.difficulty_parameters[challenge_id]; + let min_difficulty = difficulty_parameters.min_difficulty(); + let max_difficulty = difficulty_parameters.max_difficulty(); + if (0..difficulty.len()) + .into_iter() + .any(|i| difficulty[i] < min_difficulty[i] || difficulty[i] > max_difficulty[i]) + { + continue; + } + *player_algorithm_solutions + .entry(player_id.clone()) + .or_default() + .entry(algorithm_id.clone()) + .or_default() += num_solutions; + *player_solutions.entry(player_id.clone()).or_default() += num_solutions; + *player_nonces.entry(player_id.clone()).or_default() += num_nonces as u64; + + challenge_data + .qualifier_difficulties + .insert(difficulty.clone()); + } + + // check if we have enough qualifiers + let player_solution_ratio: HashMap = player_solutions + .keys() + .map(|player_id| { + ( + player_id.clone(), + player_solutions[player_id] as f64 / player_nonces[player_id] as f64, + ) + }) + .collect(); + let player_qualifiers: HashMap = player_solution_ratio + .keys() + .map(|player_id| { + ( + player_id.clone(), + if player_nonces[player_id] >= min_num_nonces + && player_solution_ratio[player_id] >= min_solution_ratio + { + max_qualifiers_by_player[player_id].min(player_solutions[player_id]) + } else { + 0 + }, + ) + }) + .collect(); + + let num_qualifiers = player_qualifiers.values().sum::(); + if num_qualifiers >= config.opow.total_qualifiers_threshold + || frontier_idx == solutions_by_frontier_idx.len() - 1 + { + let mut sum_weighted_solution_ratio = 0.0; + for player_id in player_qualifiers.keys() { + let opow_data = active_opow_block_data.get_mut(player_id).unwrap(); + opow_data + .num_qualifiers_by_challenge + .insert(challenge_id.clone(), player_qualifiers[player_id]); + opow_data + .solution_ratio_by_challenge + .insert(challenge_id.clone(), player_solution_ratio[player_id]); + + sum_weighted_solution_ratio += + player_solution_ratio[player_id] * player_qualifiers[player_id] as f64; + + if player_qualifiers[player_id] > 0 { + for algorithm_id in player_algorithm_solutions[player_id].keys() { + let algorithm_data = + active_algorithms_block_data.get_mut(algorithm_id).unwrap(); + + algorithm_data.num_qualifiers_by_player.insert( + player_id.clone(), + (player_qualifiers[player_id] as f64 + * player_algorithm_solutions[player_id][algorithm_id] as f64 + / player_solutions[player_id] as f64) + .ceil() as u32, + ); + } + } + } + challenge_data.num_qualifiers = num_qualifiers; + challenge_data.average_solution_ratio = if num_qualifiers == 0 { + 0.0 + } else { + sum_weighted_solution_ratio / num_qualifiers as f64 + }; break; } - let difficulty_parameters = &config.challenges.difficulty_parameters[challenge_id]; - let min_difficulty = difficulty_parameters.min_difficulty(); - let max_difficulty = difficulty_parameters.max_difficulty(); - if (0..difficulty.len()) - .into_iter() - .any(|i| difficulty[i] < min_difficulty[i] || difficulty[i] > max_difficulty[i]) - { - continue; - } - curr_frontier_index = frontier_indexes[difficulty]; - let player_data = active_opow_block_data.get_mut(player_id).unwrap(); - let algorithm_data = active_algorithms_block_data.get_mut(algorithm_id).unwrap(); - - let max_qualifiers = max_qualifiers_by_player.get(player_id).unwrap().clone(); - let num_qualifiers = num_solutions.min(max_qualifiers); - max_qualifiers_by_player.insert(player_id.clone(), max_qualifiers - num_qualifiers); - - if num_qualifiers > 0 { - *player_data - .num_qualifiers_by_challenge - .entry(challenge_id.clone()) - .or_default() += num_qualifiers; - *algorithm_data - .num_qualifiers_by_player - .entry(player_id.clone()) - .or_default() += num_qualifiers; - challenge_data.num_qualifiers += num_qualifiers; - } - challenge_data - .qualifier_difficulties - .insert(difficulty.clone()); } } @@ -246,18 +314,6 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { return; } - let num_qualifiers_by_challenge: HashMap = active_challenge_ids - .iter() - .map(|challenge_id| { - ( - challenge_id.clone(), - active_challenges_block_data[challenge_id] - .num_qualifiers - .clone(), - ) - }) - .collect(); - for player_id in active_opow_ids.iter() { let opow_data = active_opow_block_data.get_mut(player_id).unwrap(); opow_data.self_deposit = self_deposit[player_id].clone(); @@ -303,45 +359,52 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { for player_id in active_opow_ids.iter() { let opow_data = active_opow_block_data.get_mut(player_id).unwrap(); - let mut percent_qualifiers = Vec::::new(); + let mut challenge_factors = Vec::::new(); for challenge_id in active_challenge_ids.iter() { - let num_qualifiers = num_qualifiers_by_challenge[challenge_id]; - let num_qualifiers_by_player = *opow_data - .num_qualifiers_by_challenge - .get(challenge_id) - .unwrap_or(&0); - - percent_qualifiers.push(if num_qualifiers_by_player == 0 { - PreciseNumber::from(0) + let challenge_data = active_challenges_block_data.get(challenge_id).unwrap(); + challenge_factors.push(if challenge_data.num_qualifiers == 0 { + zero.clone() } else { - PreciseNumber::from(num_qualifiers_by_player) / PreciseNumber::from(num_qualifiers) + let fraction_qualifiers = PreciseNumber::from( + *opow_data + .num_qualifiers_by_challenge + .get(challenge_id) + .unwrap_or(&0), + ) / PreciseNumber::from(challenge_data.num_qualifiers); + let reliability = *opow_data + .solution_ratio_by_challenge + .get(challenge_id) + .unwrap_or(&0.0) + / challenge_data.average_solution_ratio; + fraction_qualifiers + * PreciseNumber::from_f64(reliability.min(config.opow.max_reliability)) }); } - let mut percent_deposit = if total_deposit == zero { + let mut deposit_factor = if total_deposit == zero { zero.clone() } else { opow_data.delegated_weighted_deposit / total_deposit }; - let mean_percent_qualifiers = percent_qualifiers.arithmetic_mean(); + let mean_challenge_factor = challenge_factors.arithmetic_mean(); let max_deposit_to_qualifier_ratio = PreciseNumber::from_f64(config.opow.max_deposit_to_qualifier_ratio); - if mean_percent_qualifiers == zero { - percent_deposit = zero.clone(); - } else if percent_deposit / mean_percent_qualifiers > max_deposit_to_qualifier_ratio { - percent_deposit = mean_percent_qualifiers * max_deposit_to_qualifier_ratio; + if mean_challenge_factor == zero { + deposit_factor = zero.clone(); + } else if deposit_factor / mean_challenge_factor > max_deposit_to_qualifier_ratio { + deposit_factor = mean_challenge_factor * max_deposit_to_qualifier_ratio; } - let sum_percent_qualifiers: PreciseNumber = percent_qualifiers.iter().cloned().sum(); - let weighted_mean = (sum_percent_qualifiers - + percent_deposit * PreciseNumber::from_f64(config.opow.deposit_multiplier)) + let sum_challenge_factors: PreciseNumber = challenge_factors.iter().cloned().sum(); + let weighted_mean = (sum_challenge_factors + + deposit_factor * PreciseNumber::from_f64(config.opow.deposit_multiplier)) / PreciseNumber::from_f64( active_challenge_ids.len() as f64 + config.opow.deposit_multiplier, ); - let mut qualifiers_and_deposit = percent_qualifiers; - qualifiers_and_deposit.push(percent_deposit); - let mean = qualifiers_and_deposit.arithmetic_mean(); - let variance = qualifiers_and_deposit.variance(); + let mut all_factors = challenge_factors; + all_factors.push(deposit_factor); + let mean = all_factors.arithmetic_mean(); + let variance = all_factors.variance(); let cv_sqr = if mean == zero { zero.clone() } else { diff --git a/tig-protocol/src/contracts/players.rs b/tig-protocol/src/contracts/players.rs index f25b789..0eccde6 100644 --- a/tig-protocol/src/contracts/players.rs +++ b/tig-protocol/src/contracts/players.rs @@ -1,109 +1,57 @@ use crate::context::*; use anyhow::{anyhow, Result}; use logging_timer::time; -use std::{ - collections::HashMap, - time::{SystemTime, UNIX_EPOCH}, -}; +use std::collections::HashMap; use tig_structs::core::*; -use tig_utils::*; #[time] -pub async fn submit_topup( +pub async fn set_coinbase( ctx: &T, player_id: String, - tx_hash: String, - log_idx: Option, -) -> Result { + coinbase: HashMap, +) -> Result<()> { let config = ctx.get_config().await; + let latest_block_id = ctx.get_latest_block_id().await; + let latest_block_details = ctx.get_block_details(&latest_block_id).await.unwrap(); + let player_state = ctx.get_player_state(&player_id).await.unwrap(); - let transfer = get_transfer(&config.erc20.rpc_url, &tx_hash, log_idx.clone()) - .await - .map_err(|_| anyhow!("Invalid transaction: {}", tx_hash))?; - if transfer.erc20 != config.erc20.token_address { - return Err(anyhow!("Transfer asset must be TIG token")); - } - if transfer.sender != player_id { - return Err(anyhow!("Transfer must be from player")); - } - if transfer.receiver != config.topups.topup_address { - return Err(anyhow!("Transfer must send to topup_address")); - } - if transfer.amount < config.topups.min_topup_amount { - return Err(anyhow!("Transfer must be at least min_topup_amount")); - } - let topup_id = ctx - .add_topup_to_mempool(TopUpDetails { - player_id, - tx_hash, - amount: transfer.amount, - log_idx: transfer.log_idx, - }) - .await?; - Ok(topup_id) -} - -#[time] -pub async fn submit_deposit( - ctx: &T, - player_id: String, - tx_hash: String, - log_idx: Option, -) -> Result { - let config = ctx.get_config().await; - - let linear_lock = get_linear_lock(&config.erc20.rpc_url, &tx_hash, log_idx.clone()) - .await - .map_err(|_| anyhow!("Invalid transaction: {}", tx_hash))?; - if linear_lock.locker != config.deposits.lock_address { - return Err(anyhow!("Deposit must be LinearLock of TIG token")); - } - if linear_lock.erc20 != config.erc20.token_address { - return Err(anyhow!("LinearLock asset must be TIG token")); - } - if linear_lock.owner != player_id { - return Err(anyhow!("LinearLock must be owned by player")); - } - if linear_lock.can_cancel { - return Err(anyhow!("LinearLock must not be cancelable")); - } - if linear_lock.can_transfer { - return Err(anyhow!("LinearLock with transferrable not supported")); - } - if linear_lock.cliff_timestamp != 0 { - return Err(anyhow!("LinearLock with cliff not supported")); - } - if linear_lock.amount < config.deposits.min_lock_amount { - return Err(anyhow!("LinearLock must be at least min_lock_amount")); - } - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - if linear_lock.end_timestamp <= now { - return Err(anyhow!("LinearLock is already expired")); - } - - let min_duration = config.deposits.min_lock_period - * config.rounds.blocks_per_round - * config.rounds.seconds_between_blocks; - if linear_lock.end_timestamp - linear_lock.start_timestamp < min_duration as u64 { + if coinbase.len() > config.opow.max_coinbase_outputs { return Err(anyhow!( - "LinearLock must be at least {} round", - config.deposits.min_lock_period + "Cannot split coinbase to more than {} players", + config.opow.max_coinbase_outputs )); } - let deposit_id = ctx - .add_deposit_to_mempool(DepositDetails { - player_id, - tx_hash, - amount: linear_lock.amount, - log_idx: linear_lock.log_idx, - start_timestamp: linear_lock.start_timestamp, - end_timestamp: linear_lock.end_timestamp, - }) - .await?; - Ok(deposit_id) + if let Some(curr_coinbase) = &player_state.coinbase { + if latest_block_details.height - curr_coinbase.block_set + < config.opow.coinbase_update_period + { + return Err(anyhow!( + "Can only update coinbase every {} blocks. Please wait {} blocks", + config.opow.coinbase_update_period, + config.opow.coinbase_update_period + - (latest_block_details.height - curr_coinbase.block_set) + )); + } + } + + if coinbase.values().any(|&v| v <= 0.0 || v > 1.0) { + return Err(anyhow!( + "Fraction must be greater than 0.0 and less than or equal to 1.0" + )); + } + + if coinbase.values().cloned().sum::() > 1.0 { + return Err(anyhow!("Total fraction cannot exceed 1.0")); + } + + for output in coinbase.keys() { + if ctx.get_player_details(output).await.is_none() { + return Err(anyhow!("Player '{}' is invalid or not registered", output)); + } + } + + ctx.set_player_coinbase(player_id, coinbase).await?; + Ok(()) } #[time] @@ -136,12 +84,14 @@ pub async fn set_delegatees( } } - if delegatees.values().any(|&v| v <= 0.0) { - return Err(anyhow!("Fraction to delegate cannot be zero or negative")); + if delegatees.values().any(|&v| v <= 0.0 || v > 1.0) { + return Err(anyhow!( + "Fraction must be greater than 0.0 and less than or equal to 1.0" + )); } if delegatees.values().cloned().sum::() > 1.0 { - return Err(anyhow!("Total fraction to delegate cannot exceed 1.0")); + return Err(anyhow!("Total fraction cannot exceed 1.0")); } for delegatee in delegatees.keys() { @@ -271,33 +221,48 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { } for deposit in active_deposit_details.values() { - let total_time = PreciseNumber::from(deposit.end_timestamp - deposit.start_timestamp); - for i in 0..lock_period_cap { - if i + 1 < lock_period_cap && round_timestamps[i + 1] <= deposit.start_timestamp { - continue; + match &deposit.r#type { + DepositType::Linear { + start_timestamp, + end_timestamp, + } => { + let total_time = PreciseNumber::from(end_timestamp - start_timestamp); + for i in 0..lock_period_cap { + if i + 1 < lock_period_cap && round_timestamps[i + 1] <= *start_timestamp { + continue; + } + if round_timestamps[i] >= *end_timestamp { + break; + } + let start = if round_timestamps[i] <= *start_timestamp { + *start_timestamp + } else { + round_timestamps[i] + }; + // all deposits above max_lock_period_rounds get the same max weight + let end = + if round_timestamps[i + 1] >= *end_timestamp || i + 1 == lock_period_cap { + *end_timestamp + } else { + round_timestamps[i + 1] + }; + let amount = deposit.amount * PreciseNumber::from(end - start) / total_time; + let weight = PreciseNumber::from(i + 1); + let player_data = active_players_block_data + .get_mut(&deposit.player_id) + .unwrap(); + *player_data.deposit_by_locked_period.get_mut(i).unwrap() += amount; + player_data.weighted_deposit += amount * weight; + } } - if round_timestamps[i] >= deposit.end_timestamp { - break; + DepositType::Lock { .. } => { + let weight = PreciseNumber::from(config.deposits.token_locker_weight); + let player_data = active_players_block_data + .get_mut(&deposit.player_id) + .unwrap(); + *player_data.deposit_by_locked_period.get_mut(3).unwrap() += deposit.amount; + player_data.weighted_deposit += deposit.amount * weight; } - let start = if round_timestamps[i] <= deposit.start_timestamp { - deposit.start_timestamp - } else { - round_timestamps[i] - }; - // all deposits above max_lock_period_rounds get the same max weight - let end = - if round_timestamps[i + 1] >= deposit.end_timestamp || i + 1 == lock_period_cap { - deposit.end_timestamp - } else { - round_timestamps[i + 1] - }; - let amount = deposit.amount * PreciseNumber::from(end - start) / total_time; - let weight = PreciseNumber::from(i + 1); - let player_data = active_players_block_data - .get_mut(&deposit.player_id) - .unwrap(); - *player_data.deposit_by_locked_period.get_mut(i).unwrap() += amount; - player_data.weighted_deposit += amount * weight; } } -} \ No newline at end of file +} diff --git a/tig-protocol/src/contracts/rewards.rs b/tig-protocol/src/contracts/rewards.rs index 2c6bea1..84ebae1 100644 --- a/tig-protocol/src/contracts/rewards.rs +++ b/tig-protocol/src/contracts/rewards.rs @@ -136,24 +136,44 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { let zero = PreciseNumber::from(0); for (delegatee, opow_data) in active_opow_block_data.iter_mut() { opow_data.reward = opow_data.influence * reward_pool; - opow_data.reward_share = active_players_state[delegatee] - .reward_share + + if opow_data.reward == zero { + continue; + } + + if opow_data.delegated_weighted_deposit > zero { + opow_data.reward_share = opow_data.reward + * PreciseNumber::from_f64( + active_players_state[delegatee] + .reward_share + .as_ref() + .map_or(config.deposits.default_reward_share, |x| x.value) + .clone(), + ) + } + let coinbase_amount = opow_data.reward - opow_data.reward_share; + + for (output, fraction) in active_players_state[delegatee] + .coinbase .as_ref() - .map_or(config.deposits.default_reward_share, |x| x.value) - .clone(); + .map_or_else( + || HashMap::from([(delegatee.clone(), 1.0)]), + |x| x.value.clone(), + ) + .iter() + { + let fraction = PreciseNumber::from_f64(*fraction); + let amount = coinbase_amount * fraction; + opow_data.coinbase.insert(output.clone(), amount.clone()); - let shared_amount = if opow_data.delegated_weighted_deposit == zero { - zero.clone() - } else { - opow_data.reward * PreciseNumber::from_f64(opow_data.reward_share) - }; - active_players_block_data - .get_mut(delegatee) - .unwrap() - .reward_by_type - .insert(RewardType::Benchmarker, opow_data.reward - shared_amount); + let player_data = active_players_block_data.get_mut(output).unwrap(); + *player_data + .reward_by_type + .entry(RewardType::Benchmarker) + .or_insert(zero.clone()) += amount; + } - if shared_amount == zero { + if opow_data.reward_share == zero { continue; } @@ -163,8 +183,9 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { *player_data .reward_by_type .entry(RewardType::Delegator) - .or_insert(zero.clone()) += shared_amount * fraction * player_data.weighted_deposit - / opow_data.delegated_weighted_deposit; + .or_insert(zero.clone()) += + opow_data.reward_share * fraction * player_data.weighted_deposit + / opow_data.delegated_weighted_deposit; } } } diff --git a/tig-protocol/src/lib.rs b/tig-protocol/src/lib.rs index accceaf..937944d 100644 --- a/tig-protocol/src/lib.rs +++ b/tig-protocol/src/lib.rs @@ -5,7 +5,7 @@ use context::*; pub use contracts::{ algorithms::{submit_algorithm, submit_binary, submit_breakthrough}, benchmarks::{submit_benchmark, submit_fraud, submit_precommit, submit_proof}, - players::{set_delegatees, set_reward_share, set_vote, submit_deposit, submit_topup}, + players::{set_coinbase, set_delegatees, set_reward_share, set_vote}, }; pub async fn add_block(ctx: &T) { diff --git a/tig-structs/src/config.rs b/tig-structs/src/config.rs index 2c82563..734caf2 100644 --- a/tig-structs/src/config.rs +++ b/tig-structs/src/config.rs @@ -50,6 +50,7 @@ serializable_struct_with_getters! { delegatees_update_period: u32, delegatee_min_deposit: PreciseNumber, max_delegations: usize, + token_locker_weight: u32, } } serializable_struct_with_getters! { @@ -109,6 +110,11 @@ serializable_struct_with_getters! { max_deposit_to_qualifier_ratio: f64, deposit_multiplier: f64, deposit_to_cutoff_ratio: f64, + max_coinbase_outputs: usize, + coinbase_update_period: u32, + min_solution_ratio_factor: f64, + min_num_nonces: u32, + max_reliability: f64, } } serializable_struct_with_getters! { diff --git a/tig-structs/src/core.rs b/tig-structs/src/core.rs index 553972c..f551abc 100644 --- a/tig-structs/src/core.rs +++ b/tig-structs/src/core.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use std::collections::{HashMap, HashSet}; use tig_utils::{jsonify, u64s_from_str, u8s_from_str}; -pub use tig_utils::{Frontier, MerkleBranch, MerkleHash, Point, PreciseNumber, Transfer, U256}; +pub use tig_utils::{Frontier, MerkleBranch, MerkleHash, Point, PreciseNumber, U256}; serializable_struct_with_getters! { Algorithm { @@ -303,6 +303,7 @@ serializable_struct_with_getters! { ChallengeBlockData { num_qualifiers: u32, qualifier_difficulties: HashSet, + average_solution_ratio: f64, base_frontier: Frontier, scaled_frontier: Frontier, scaling_factor: f64, @@ -312,14 +313,24 @@ serializable_struct_with_getters! { } // Deposit child structs +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DepositType { + Linear { + start_timestamp: u64, + end_timestamp: u64, + }, + Lock { + eth_block_num: u64, + }, +} serializable_struct_with_getters! { DepositDetails { player_id: String, tx_hash: String, log_idx: usize, amount: PreciseNumber, - start_timestamp: u64, - end_timestamp: u64, + r#type: DepositType, } } serializable_struct_with_getters! { @@ -343,7 +354,9 @@ serializable_struct_with_getters! { self_deposit: PreciseNumber, delegated_weighted_deposit: PreciseNumber, delegators: HashSet, - reward_share: f64, + reward_share: PreciseNumber, + coinbase: HashMap, + solution_ratio_by_challenge: HashMap, imbalance: PreciseNumber, influence: PreciseNumber, reward: PreciseNumber, @@ -369,6 +382,7 @@ serializable_struct_with_getters! { delegatees: Option>>, votes: HashMap>, reward_share: Option>, + coinbase: Option>>, } } #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] diff --git a/tig-token/TokenLocker.sol b/tig-token/TokenLocker.sol new file mode 100644 index 0000000..f7b43f0 --- /dev/null +++ b/tig-token/TokenLocker.sol @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +interface ITokenLocker { + event TokensLocked(address indexed user, uint256 amount, uint256 locked); + event TokensUnlocked(address indexed user, uint256 amount, uint256 locked, uint256 withdrawableTime); + event TokensWithdrawn(address indexed user, uint256 amount); + event TokensRewarded(address indexed user, uint256 amount, uint256 claimable); + event TokensRelocked(address indexed user, uint256 amount, uint256 locked); + event TokensClaimed(address indexed user, uint256 amount, uint256 locked); + + function getNumPendingWithdrawals(address account) external view returns (uint256); + function getTimeUntilWithdrawable(address account, uint256 index) external view returns (uint256); + function lock(uint256 amount) external; + function unlock(uint256 amount) external; + function withdraw(uint256 index) external; + function relock(uint256 index) external; + function rewardTokens(address account, uint256 amount) external; + function claim(uint256 amount) external; +} + +contract TokenLocker is ITokenLocker, ReentrancyGuard, Ownable { + using SafeMath for uint256; + + struct PendingWithdrawal { + uint256 amount; + uint256 timeWithdrawable; + } + + IERC20 public immutable token; + uint256 public immutable pendingPeriod; + + uint256 public totalLocked; + uint256 public totalPendingWithdrawal; + uint256 public totalClaimable; + + mapping(address => uint256) public locked; + mapping(address => uint256) public claimable; + mapping(address => PendingWithdrawal[]) public pendingWithdrawals; + + constructor(address tokenAddress, uint256 _pendingPeriod) public Ownable(msg.sender) { + token = IERC20(tokenAddress); + pendingPeriod = _pendingPeriod; + } + + function getNumPendingWithdrawals(address account) external view override returns (uint256) { + return pendingWithdrawals[account].length; + } + + function getTimeUntilWithdrawable(address account, uint256 index) external view returns (uint256) { + require(index < pendingWithdrawals[account].length, "Invalid withdrawal index"); + + uint256 withdrawableTime = pendingWithdrawals[account][index].timeWithdrawable; + + if (block.timestamp >= withdrawableTime) { + return 0; + } + + return withdrawableTime.sub(block.timestamp); + } + + function lock(uint256 amount) external override nonReentrant { + require(amount > 0, "Amount must be greater than 0"); + + require(token.transferFrom(msg.sender, address(this), amount), "Transfer failed"); + + locked[msg.sender] = locked[msg.sender].add(amount); + totalLocked = totalLocked.add(amount); + + emit TokensLocked(msg.sender, amount, locked[msg.sender]); + } + + function unlock(uint256 amount) external override nonReentrant { + require(amount > 0, "Amount must be greater than 0"); + require(locked[msg.sender] >= amount, "Insufficient locked balance"); + + locked[msg.sender] = locked[msg.sender].sub(amount); + totalLocked = totalLocked.sub(amount); + totalPendingWithdrawal = totalPendingWithdrawal.add(amount); + + uint256 timeWithdrawable = block.timestamp.add(pendingPeriod); + pendingWithdrawals[msg.sender].push(PendingWithdrawal({ + amount: amount, + timeWithdrawable: timeWithdrawable + })); + + emit TokensUnlocked(msg.sender, amount, locked[msg.sender], timeWithdrawable); + } + + function withdraw(uint256 index) external override nonReentrant { + require(index < pendingWithdrawals[msg.sender].length, "Invalid withdrawal index"); + PendingWithdrawal[] storage userWithdrawals = pendingWithdrawals[msg.sender]; + PendingWithdrawal memory withdrawal = userWithdrawals[index]; + + require(block.timestamp >= withdrawal.timeWithdrawable, "Withdrawal not yet available"); + + uint256 amount = withdrawal.amount; + + // Swap and pop + userWithdrawals[index] = userWithdrawals[userWithdrawals.length - 1]; + userWithdrawals.pop(); + + totalPendingWithdrawal = totalPendingWithdrawal.sub(amount); + + require(token.transfer(msg.sender, amount), "Transfer failed"); + + emit TokensWithdrawn(msg.sender, amount); + } + + function relock(uint256 index) external override nonReentrant { + require(index < pendingWithdrawals[msg.sender].length, "Invalid withdrawal index"); + PendingWithdrawal[] storage userWithdrawals = pendingWithdrawals[msg.sender]; + PendingWithdrawal memory withdrawal = userWithdrawals[index]; + + uint256 amount = withdrawal.amount; + + // Swap and pop + userWithdrawals[index] = userWithdrawals[userWithdrawals.length - 1]; + userWithdrawals.pop(); + + // Update state + totalPendingWithdrawal = totalPendingWithdrawal.sub(amount); + locked[msg.sender] = locked[msg.sender].add(amount); + totalLocked = totalLocked.add(amount); + + emit TokensRelocked(msg.sender, amount, locked[msg.sender]); + } + + function rewardTokens(address account, uint256 amount) external override onlyOwner nonReentrant { + require(account != address(0), "Invalid address"); + require(amount > 0, "Amount must be greater than 0"); + + require(token.transferFrom(msg.sender, address(this), amount), "Transfer failed"); + + claimable[account] = claimable[account].add(amount); + totalClaimable = totalClaimable.add(amount); + + emit TokensRewarded(account, amount, claimable[account]); + } + + function claim(uint256 amount) external override nonReentrant { + require(amount > 0, "Amount must be greater than 0"); + require(claimable[msg.sender] >= amount, "Insufficient claimable balance"); + + claimable[msg.sender] = claimable[msg.sender].sub(amount); + totalClaimable = totalClaimable.sub(amount); + + locked[msg.sender] = locked[msg.sender].add(amount); + totalLocked = totalLocked.add(amount); + + emit TokensClaimed(msg.sender, amount, locked[msg.sender]); + } +} \ No newline at end of file diff --git a/tig-utils/src/eth.rs b/tig-utils/src/eth.rs index 3542f67..4e1c833 100644 --- a/tig-utils/src/eth.rs +++ b/tig-utils/src/eth.rs @@ -1,284 +1,122 @@ -use crate::number::PreciseNumber; -use serde::{Deserialize, Serialize}; +use std::str::FromStr; -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct Transfer { - pub erc20: String, - pub sender: String, - pub receiver: String, - pub amount: PreciseNumber, - pub log_idx: usize, +use anyhow::{anyhow, Result}; +use hex::{self, ToHex}; +use web3::{contract::*, signing::*, types::*}; + +pub fn recover_address_from_msg_and_sig(msg: &str, sig: &str) -> Result { + let hash_msg = hash_message(msg.as_bytes()); + let recovery = Recovery::from_raw_signature( + hash_msg, + hex::decode(sig.trim_start_matches("0x")) + .map_err(|e| web3::Error::InvalidResponse(e.to_string()))?, + )?; + let (signature, recovery_id) = recovery.as_signature().unwrap(); + let address = recover(hash_msg.as_bytes(), &signature, recovery_id)?; + Ok(format!("0x{}", address.encode_hex::())) } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct LinearLock { - pub locker: String, - pub erc20: String, - pub owner: String, - pub can_cancel: bool, - pub can_transfer: bool, - pub start_timestamp: u64, - pub cliff_timestamp: u64, - pub end_timestamp: u64, - pub amount: PreciseNumber, - pub log_idx: usize, -} - -#[cfg(feature = "web3")] -mod web3_feature { - use std::str::FromStr; - - use crate::PreciseNumber; - use anyhow::{anyhow, Result}; - use hex::{self, ToHex}; - use web3::{contract::*, signing::*, types::*}; - - pub fn recover_address_from_msg_and_sig(msg: &str, sig: &str) -> Result { - let hash_msg = hash_message(msg.as_bytes()); - let recovery = Recovery::from_raw_signature( - hash_msg, - hex::decode(sig.trim_start_matches("0x")) - .map_err(|e| web3::Error::InvalidResponse(e.to_string()))?, - )?; - let (signature, recovery_id) = recovery.as_signature().unwrap(); - let address = recover(hash_msg.as_bytes(), &signature, recovery_id)?; - Ok(format!("0x{}", address.encode_hex::())) +pub const GNOSIS_SAFE_ABI: &str = r#"[ + { + "inputs": [ + { + "name": "_dataHash", + "type": "bytes32" + }, + { + "name": "_signature", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "name": "", + "type": "bytes4" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" } +]"#; - pub const ERC20_TRANSFER_TOPIC: &str = - "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; +pub async fn is_valid_gnosis_safe_sig( + rpc_url: &str, + address: &str, + msg: &str, + sig: &str, +) -> Result<()> { + let transport = web3::transports::Http::new(rpc_url)?; + let eth = web3::Web3::new(transport).eth(); - pub async fn get_transfer( - rpc_url: &str, - tx_hash: &str, - log_idx: Option, - ) -> Result { - let transport = web3::transports::Http::new(rpc_url)?; - let eth = web3::Web3::new(transport).eth(); - - let tx_hash = H256::from_slice(hex::decode(tx_hash.trim_start_matches("0x"))?.as_slice()); - let receipt = eth - .transaction_receipt(tx_hash) - .await? - .ok_or_else(|| anyhow!("Receipt for transaction {} not found", tx_hash))?; - - if !receipt.status.is_some_and(|x| x.as_u64() == 1) { - return Err(anyhow!("Transaction not confirmed")); - } - - // Find the Transfer event log - let transfer_topic = H256::from_slice(&hex::decode(ERC20_TRANSFER_TOPIC)?); - let (log_idx, transfer_log) = receipt - .logs - .iter() - .enumerate() - .find(|(idx, log)| { - (log_idx.is_none() || log_idx.is_some_and(|i| i == *idx)) - && !log.topics.is_empty() - && log.topics[0] == transfer_topic - }) - .ok_or_else(|| anyhow!("No ERC20 transfer event found"))?; - - if transfer_log.topics.len() != 3 { - return Err(anyhow!("Invalid Transfer event format")); - } - - // Extract transfer details from the event - let erc20 = format!("0x{}", hex::encode(&transfer_log.address.as_bytes())); - let sender = format!( - "0x{}", - hex::encode(&transfer_log.topics[1].as_bytes()[12..]) - ); - let receiver = format!( - "0x{}", - hex::encode(&transfer_log.topics[2].as_bytes()[12..]) - ); - let amount = PreciseNumber::from_hex_str(&hex::encode(&transfer_log.data.0))?; - - Ok(super::Transfer { - erc20, - sender, - receiver, - amount, - log_idx, - }) - } - - pub const SABLIERV2_CREATELOCKUPLINEARSTREAM_TOPIC: &str = - "44cb432df42caa86b7ec73644ab8aec922bc44c71c98fc330addc75b88adbc7c"; - - pub async fn get_linear_lock( - rpc_url: &str, - tx_hash: &str, - log_idx: Option, - ) -> Result { - let transport = web3::transports::Http::new(rpc_url)?; - let eth = web3::Web3::new(transport).eth(); - - let tx_hash = H256::from_slice(hex::decode(tx_hash.trim_start_matches("0x"))?.as_slice()); - let receipt = eth - .transaction_receipt(tx_hash) - .await? - .ok_or_else(|| anyhow!("Receipt for transaction {} not found", tx_hash))?; - - if !receipt.status.is_some_and(|x| x.as_u64() == 1) { - return Err(anyhow!("Transaction not confirmed")); - } - - // Find the Transfer event log - let linear_lock_topic = - H256::from_slice(&hex::decode(SABLIERV2_CREATELOCKUPLINEARSTREAM_TOPIC)?); - let (log_idx, transfer_log) = receipt - .logs - .iter() - .enumerate() - .find(|(idx, log)| { - (log_idx.is_none() || log_idx.is_some_and(|i| i == *idx)) - && !log.topics.is_empty() - && log.topics[0] == linear_lock_topic - }) - .ok_or_else(|| anyhow!("No ERC20 transfer event found"))?; - - if transfer_log.topics.len() != 4 { - return Err(anyhow!("Invalid CreateLinearLockStream event format")); - } - - // Extract transfer details from the event - let locker = format!("0x{}", hex::encode(&transfer_log.address.as_bytes())); - let owner = format!( - "0x{}", - hex::encode(&transfer_log.topics[2].as_bytes()[12..]) - ); - let erc20 = format!( - "0x{}", - hex::encode(&transfer_log.topics[3].as_bytes()[12..]) - ); - let amount = PreciseNumber::from_hex_str(&hex::encode(&transfer_log.data.0[64..96]))?; - let can_cancel = transfer_log.data.0[159] == 1; - let can_transfer = transfer_log.data.0[191] == 1; - let start_timestamp = u64::from_be_bytes(transfer_log.data.0[216..224].try_into().unwrap()); - let cliff_timestamp = u64::from_be_bytes(transfer_log.data.0[248..256].try_into().unwrap()); - let end_timestamp = u64::from_be_bytes(transfer_log.data.0[280..288].try_into().unwrap()); - - Ok(super::LinearLock { - locker, - erc20, - owner, - amount, - can_cancel, - can_transfer, - start_timestamp, - cliff_timestamp, - end_timestamp, - log_idx, - }) - } - - pub const GNOSIS_SAFE_ABI: &str = r#"[ - { - "inputs": [ - { - "name": "_dataHash", - "type": "bytes32" - }, - { - "name": "_signature", - "type": "bytes" - } - ], - "name": "isValidSignature", - "outputs": [ - { - "name": "", - "type": "bytes4" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - } - ]"#; - - pub async fn is_valid_gnosis_safe_sig( - rpc_url: &str, - address: &str, - msg: &str, - sig: &str, - ) -> Result<()> { - let transport = web3::transports::Http::new(rpc_url)?; - let eth = web3::Web3::new(transport).eth(); - - let gnosis_safe = Contract::from_json( - eth.clone(), - H160::from_str(&address)?, - GNOSIS_SAFE_ABI.as_bytes(), + let gnosis_safe = Contract::from_json( + eth.clone(), + H160::from_str(&address)?, + GNOSIS_SAFE_ABI.as_bytes(), + ) + .unwrap(); + let result: Result, _> = gnosis_safe + .query( + "isValidSignature", + ( + hash_message(msg.as_bytes()), + hex::decode(sig.trim_start_matches("0x"))?, + ), + None, + Options::default(), + None, ) - .unwrap(); - let result: Result, _> = gnosis_safe - .query( - "isValidSignature", - ( - hash_message(msg.as_bytes()), - hex::decode(sig.trim_start_matches("0x"))?, - ), - None, - Options::default(), - None, - ) - .await; - match result { - Ok(_) => Ok(()), - Err(e) => Err(anyhow!("Failed query isValidSignature: {:?}", e)), - } - } - - pub const ENS_REVERSE_RECORDS_ADDRESS: &str = "0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C"; - pub const ENS_REVERSE_RECORDS_ABI: &str = r#"[ - { - "inputs": [ - { - "internalType": "address[]", - "name": "addresses", - "type": "address[]" - } - ], - "name": "getNames", - "outputs": [ - { - "internalType": "string[]", - "name": "r", - "type": "string[]" - } - ], - "stateMutability": "view", - "type": "function" - } - ]"#; - - pub async fn lookup_ens_name(rpc_url: &str, address: &str) -> Result> { - let transport = web3::transports::Http::new(rpc_url)?; - let eth = web3::Web3::new(transport).eth(); - - let reverse_records = Contract::from_json( - eth.clone(), - H160::from_str(ENS_REVERSE_RECORDS_ADDRESS)?, - ENS_REVERSE_RECORDS_ABI.as_bytes(), - )?; - - let addresses = vec![H160::from_str(address.trim_start_matches("0x"))?]; - - let names: Vec = reverse_records - .query("getNames", (addresses,), None, Options::default(), None) - .await?; - - // Return first name or none if empty - Ok(if !names[0].is_empty() { - Some(names[0].clone()) - } else { - None - }) + .await; + match result { + Ok(_) => Ok(()), + Err(e) => Err(anyhow!("Failed query isValidSignature: {:?}", e)), } } -#[cfg(feature = "web3")] -pub use web3_feature::*; +pub const ENS_REVERSE_RECORDS_ADDRESS: &str = "0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C"; +pub const ENS_REVERSE_RECORDS_ABI: &str = r#"[ + { + "inputs": [ + { + "internalType": "address[]", + "name": "addresses", + "type": "address[]" + } + ], + "name": "getNames", + "outputs": [ + { + "internalType": "string[]", + "name": "r", + "type": "string[]" + } + ], + "stateMutability": "view", + "type": "function" + } +]"#; + +pub async fn lookup_ens_name(rpc_url: &str, address: &str) -> Result> { + let transport = web3::transports::Http::new(rpc_url)?; + let eth = web3::Web3::new(transport).eth(); + + let reverse_records = Contract::from_json( + eth.clone(), + H160::from_str(ENS_REVERSE_RECORDS_ADDRESS)?, + ENS_REVERSE_RECORDS_ABI.as_bytes(), + )?; + + let addresses = vec![H160::from_str(address.trim_start_matches("0x"))?]; + + let names: Vec = reverse_records + .query("getNames", (addresses,), None, Options::default(), None) + .await?; + + // Return first name or none if empty + Ok(if !names[0].is_empty() { + Some(names[0].clone()) + } else { + None + }) +} diff --git a/tig-utils/src/lib.rs b/tig-utils/src/lib.rs index ff95f59..d62ac37 100644 --- a/tig-utils/src/lib.rs +++ b/tig-utils/src/lib.rs @@ -1,4 +1,6 @@ +#[cfg(feature = "web3")] mod eth; +#[cfg(feature = "web3")] pub use eth::*; mod hash; pub use hash::*; diff --git a/tig-utils/tests/eth.rs b/tig-utils/tests/eth.rs index 07f184a..53f1b56 100644 --- a/tig-utils/tests/eth.rs +++ b/tig-utils/tests/eth.rs @@ -55,92 +55,6 @@ mod tests { ); } - #[tokio::test] - async fn test_get_transfer() { - assert_eq!( - tig_utils::get_transfer( - "https://mainnet.base.org", - "0xb02b7c2a4bf72f7b5e96dcfa64d42cf75b765657998c9168beddcf06811b3701", - None - ) - .await - .unwrap(), - tig_utils::Transfer { - erc20: "0x0c03ce270b4826ec62e7dd007f0b716068639f7b".to_string(), - sender: "0x691108d12348ef0a153896492d96ff92bce90fe8".to_string(), - receiver: "0x4e14297b4a5f7ab2e3c32ba262df2a2f8e367111".to_string(), - amount: tig_utils::PreciseNumber::from_hex_str( - "00000000000000000000000000000000000000000000000178f3b8cd09d1681e" - ) - .unwrap(), - log_idx: 0 - } - ); - assert_eq!( - tig_utils::get_transfer( - "https://mainnet.base.org", - "0xf68188c3913a45236a9435ac7b947448d607a2e7894c6dc205d45a3e3475dd9b", - Some(5), - ) - .await - .unwrap(), - tig_utils::Transfer { - erc20: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913".to_string(), - sender: "0x6cdcb1c4a4d1c3c6d054b27ac5b77e89eafb971d".to_string(), - receiver: "0x042c37762d1d126bc61eac2f5ceb7a96318f5db9".to_string(), - amount: tig_utils::PreciseNumber::from_hex_str( - "00000000000000000000000000000000000000000000000000000000000dfec2" - ) - .unwrap(), - log_idx: 5 - } - ); - assert_eq!( - tig_utils::get_transfer( - "https://sepolia.base.org", - "0x093aa07701f2cb1ef62f0efcf101588898d6d2869edf66b8efc23969a15c218f", - None, - ) - .await - .unwrap(), - tig_utils::Transfer { - erc20: "0x3366feee9bbe5b830df9e1fa743828732b13959a".to_string(), - sender: "0x26979f7282fc78cc83a74c5aeb317e7c13d33235".to_string(), - receiver: "0xc30edf0147c46d0a5f79bfe9b15ce9de9b8879be".to_string(), - amount: tig_utils::PreciseNumber::from(123), - log_idx: 0, - } - ); - } - - #[tokio::test] - async fn test_get_linearlock() { - assert_eq!( - tig_utils::get_linear_lock( - "https://mainnet.base.org", - "0xcb183d3a0335eb71739dc58b926c68c0382d806cab9503b5bdeeb267eb961ea1", - None - ) - .await - .unwrap(), - tig_utils::LinearLock { - locker: "0x4cb16d4153123a74bc724d161050959754f378d8".to_string(), - erc20: "0x0c03ce270b4826ec62e7dd007f0b716068639f7b".to_string(), - owner: "0xe716caba1aa085ad6f96d9027be4722a37a4e98a".to_string(), - amount: tig_utils::PreciseNumber::from_hex_str( - "0000000000000000000000000000000000000000000008890c5abfd643a573f8" - ) - .unwrap(), - can_cancel: false, - can_transfer: true, - start_timestamp: 1728860015, - cliff_timestamp: 0, - end_timestamp: 1729464815, - log_idx: 10 - } - ); - } - #[tokio::test] async fn test_lookup_ens_name() { assert_eq!( diff --git a/tig-worker/src/main.rs b/tig-worker/src/main.rs index ec4eb0d..e2bea8b 100644 --- a/tig-worker/src/main.rs +++ b/tig-worker/src/main.rs @@ -326,4 +326,4 @@ fn load_wasm(wasm_path: &PathBuf) -> Vec { eprintln!("Failed to read wasm file: {}", wasm_path.display()); std::process::exit(1); }) -} \ No newline at end of file +}