use serde::Serialize;
use crate::{
CanonicalSmtInputV0, SMT_FEATURE_GATE_V0, SMT_LAYER_MARKER_V0, SMT_SCHEMA_VERSION_V0,
SmtBackendKindV0, SmtBackendSatResultV0, SmtBackendV0, SmtVerdictV0,
encoder::canonical_smt_input_with_script_v0,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LayerInversionDeclarationV0 {
pub schema_version: &'static str,
pub product: &'static str,
pub layer_marker: &'static str,
pub feature_gate: &'static str,
pub declaration_id: String,
pub layer_rank: i64,
pub source_order: i64,
}
pub fn layer_inversion_declaration_v0(
declaration_id: impl Into<String>,
layer_rank: i64,
source_order: i64,
) -> LayerInversionDeclarationV0 {
LayerInversionDeclarationV0 {
schema_version: SMT_SCHEMA_VERSION_V0,
product: "omena-smt.layer-inversion-declaration",
layer_marker: SMT_LAYER_MARKER_V0,
feature_gate: SMT_FEATURE_GATE_V0,
declaration_id: declaration_id.into(),
layer_rank,
source_order,
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LayerFlattenInversionVerdictV0 {
pub schema_version: &'static str,
pub product: &'static str,
pub layer_marker: &'static str,
pub feature_gate: &'static str,
pub backend: SmtBackendKindV0,
pub inversion_exists: bool,
pub verdict: SmtVerdictV0,
pub canonical_input: CanonicalSmtInputV0,
pub sat_result: SmtBackendSatResultV0,
}
pub fn canonical_layer_flatten_inversion_input_v0(
declarations: &[LayerInversionDeclarationV0],
) -> CanonicalSmtInputV0 {
let mut script = String::from("(set-logic QF_LIA)\n");
for (index, declaration) in declarations.iter().enumerate() {
script.push_str(&format!("(declare-const rank_{index} Int)\n"));
script.push_str(&format!("(declare-const source_{index} Int)\n"));
script.push_str(&format!(
"(assert (= rank_{index} {}))\n",
smtlib2_int_v0(declaration.layer_rank)
));
script.push_str(&format!(
"(assert (= source_{index} {}))\n",
smtlib2_int_v0(declaration.source_order)
));
}
let mut inversion_clauses = Vec::new();
for a in 0..declarations.len() {
for b in 0..declarations.len() {
if a == b {
continue;
}
inversion_clauses.push(format!(
"(and (> rank_{a} rank_{b}) (> source_{b} source_{a}))"
));
}
}
let inversion_assertion = match inversion_clauses.len() {
0 => "false".to_string(),
1 => inversion_clauses.remove(0),
_ => format!("(or {})", inversion_clauses.join(" ")),
};
script.push_str(&format!(
"(assert (! {inversion_assertion} :named cascade_layer_flatten_inversion))\n"
));
let canonical_terms = declarations
.iter()
.map(|declaration| {
format!(
"decl:{}:rank={}:source={}",
declaration.declaration_id, declaration.layer_rank, declaration.source_order
)
})
.collect();
canonical_smt_input_with_script_v0(
"layer-flatten-cascade-inversion",
"prove_layer_flatten_candidate",
canonical_terms,
script,
)
}
pub fn smt_check_layer_flatten_inversion_v0<B: SmtBackendV0>(
declarations: &[LayerInversionDeclarationV0],
backend: &B,
) -> LayerFlattenInversionVerdictV0 {
let canonical_input = canonical_layer_flatten_inversion_input_v0(declarations);
let check = backend.check_canonical_input_v0(&canonical_input);
let inversion_exists = matches!(check.sat_result, SmtBackendSatResultV0::Sat);
let verdict = match check.sat_result {
SmtBackendSatResultV0::Sat => SmtVerdictV0::Rejected,
SmtBackendSatResultV0::Unsat => SmtVerdictV0::Accepted,
SmtBackendSatResultV0::Unknown => SmtVerdictV0::Unknown,
};
LayerFlattenInversionVerdictV0 {
schema_version: crate::SMT_SCHEMA_VERSION_V0,
product: "omena-smt.layer-flatten-inversion",
layer_marker: crate::SMT_LAYER_MARKER_V0,
feature_gate: crate::SMT_FEATURE_GATE_V0,
backend: backend.backend_kind(),
inversion_exists,
verdict,
canonical_input,
sat_result: check.sat_result,
}
}
fn smtlib2_int_v0(value: i64) -> String {
if value < 0 {
format!("(- {})", value.unsigned_abs())
} else {
value.to_string()
}
}