mirror of
https://github.com/tig-pool-nk/tig-monorepo.git
synced 2026-02-21 15:31:26 +08:00
This commit is contained in:
parent
d79e03daac
commit
f4cda56d80
BIN
cuda_keyring.deb
BIN
cuda_keyring.deb
Binary file not shown.
@ -1,5 +0,0 @@
|
||||
{
|
||||
"opRetro": {
|
||||
"projectId": "0xb0898866c2c537c61b37d04916e3879ef78fed630181c1ffaf492499d174f0bf"
|
||||
}
|
||||
}
|
||||
@ -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')
|
||||
|
||||
@ -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
|
||||
curl -s https://mainnet-api.tig.foundation/get-block?include_data | jq -r '.block.data.active_ids.benchmark[]' | nl
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
292
swagger.yaml
292
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 `<block_id>` 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 `<algorithm_id>` to `uint256`
|
||||
* `opow` is a map of `<player_id>` to `uint256`
|
||||
* `players` is a map of `<player_id>` to dict of reward types (algorithm, benchmark, breakthroughs, delegator)
|
||||
|
||||
* `breakthroughs` is a map of `<breakthrough_id>` to `uint256`
|
||||
|
||||
* `opow` is a map of `<player_id>` 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 `<player_id>` to a dict of reward types (algorithm, benchmark, breakthroughs, delegator)
|
||||
|
||||
* `names` is a map of `<player_id>` 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 `<api_key>` is invalid, a `401` error will be returned
|
||||
|
||||
* `<coinbase>` is a map of `<player_id>` 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: <api_key> 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 `<api_key>` 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 '<breakthrough_name>' IS A BREAKTHROUGH`
|
||||
|
||||
* If voting no: `I hereby confirm my vote that '<breakthrough_name>' IS INELIGIBLE AS A BREAKTHROUGH`
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SetVoteRequest'
|
||||
parameters:
|
||||
- in: header
|
||||
name: X-Api-Key
|
||||
description: <api_key> 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.
|
||||
|
||||
* `<tx_hash>` 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
|
||||
|
||||
* `<tx_hash>` 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)
|
||||
* `<log_idx>` is the log index of the CreateLockupLinearStream event
|
||||
* If `<log_idx>` 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: <api_key> 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
|
||||
|
||||
* `<tx_hash>` is the id of the transaction that has sent at least `block.config.topups.topup_amount` TIG to the `block.config.topups.topup_address`
|
||||
* `<log_idx>` is the log index of the CreateLockupLinearStream event
|
||||
* If `<log_idx>` 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: <api_key> 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'
|
||||
type: string
|
||||
@ -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]
|
||||
|
||||
@ -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<Option<Solution>> {
|
||||
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::<i32>();
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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::<i32>();
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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::<i32>();
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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::<i32>();
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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::<i32>();
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = (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<bool>, 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<bool>, 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = (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<bool>, 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<bool>, 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = (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<bool>, 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<bool>, 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = (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<bool>, 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<bool>, 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = (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<bool>, 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<bool>, 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|&w| w as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|&w| w as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|&w| w as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|&w| w as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|&w| w as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|weight| *weight as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|weight| *weight as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|weight| *weight as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|weight| *weight as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<usize> = challenge.weights.iter().map(|weight| *weight as usize).collect();
|
||||
let values: Vec<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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::<challenge_name>` 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<Option<Solution>> {
|
||||
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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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::<challenge_name>` 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<Option<Solution>> {
|
||||
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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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::<challenge_name>` 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<Option<Solution>> {
|
||||
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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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::<challenge_name>` 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<Option<Solution>> {
|
||||
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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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::<challenge_name>` 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<Option<Solution>> {
|
||||
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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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::<challenge_name>` to automatically detect your algorithm's challenge
|
||||
use anyhow::{anyhow, Result};
|
||||
use tig_challenges::knapsack::{Challenge, Solution};
|
||||
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<u8> = 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::<usize>();
|
||||
|
||||
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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = 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<i32> = 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<usize>> = vec![Vec::new(); num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).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<usize> = (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<bool> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).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<usize> = (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<bool> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
let mut rng = StdRng::seed_from_u64(challenge.seeds[0] as u64);
|
||||
let num_variables = challenge.difficulty.num_variables;
|
||||
let mut variables: Vec<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).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<usize> = (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<bool> = 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<usize> = 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<CudaKernel> = 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<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
// 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).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<usize> = (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<bool> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).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<usize> = (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<bool> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).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<usize> = (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<bool> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -1,4 +0,0 @@
|
||||
mod benchmarker_outbound;
|
||||
pub use benchmarker_outbound::solve_challenge;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL};
|
||||
@ -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<Option<Solution>> {
|
||||
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<i32>> = Vec::with_capacity(clauses_.len());
|
||||
|
||||
let mut dead = false;
|
||||
|
||||
while !(dead) {
|
||||
let mut done = true;
|
||||
for c in &clauses_ {
|
||||
let mut c_: Vec<i32> = 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<usize>> = vec![vec![]; num_variables];
|
||||
let mut n_clauses: Vec<Vec<usize>> = 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<usize> = 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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::<challenge_name>` to automatically detect your algorithm's challenge
|
||||
use anyhow::{anyhow, Result};
|
||||
use tig_challenges::satisfiability::{Challenge, Solution};
|
||||
|
||||
@ -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::<challenge_name>` 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<Option<Solution>> {
|
||||
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<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).collect();
|
||||
|
||||
for _ in 0..max_flips {
|
||||
let mut unsatisfied_clauses: Vec<&Vec<i32>> = 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<i32>, 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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::<challenge_name>` 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<Option<Solution>> {
|
||||
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<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).collect();
|
||||
|
||||
for _ in 0..max_flips {
|
||||
let mut unsatisfied_clauses: Vec<&Vec<i32>> = 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<i32>, 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
@ -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::<challenge_name>` 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<Option<Solution>> {
|
||||
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<bool> = (0..num_variables).map(|_| rng.gen::<bool>()).collect();
|
||||
|
||||
for _ in 0..max_flips {
|
||||
let mut unsatisfied_clauses: Vec<&Vec<i32>> = 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<i32>, 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<CudaKernel> = None;
|
||||
|
||||
// Important! your GPU and CPU version of the algorithm should return the same result
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user