use std::{
collections::BTreeMap,
fs, io,
io::Write,
sync::{Arc, Mutex, OnceLock},
};
use ff::{Field, FromUniformBytes, PrimeField};
use goldenfile::Mint;
use midnight_curves::Fq;
use midnight_proofs::{
circuit::Layouter,
dev::cost_model::{circuit_model, CircuitModel, COST_MEASURE_END, COST_MEASURE_START},
plonk::Circuit,
};
use serde_json::{json, Map, Value};
pub fn cost_measure_start<F: Field>(layouter: &mut impl Layouter<F>) {
let _ = layouter.namespace(|| COST_MEASURE_START);
}
pub fn cost_measure_end<F: Field>(layouter: &mut impl Layouter<F>) {
let _ = layouter.namespace(|| COST_MEASURE_END);
}
#[cfg(not(target_os = "windows"))]
pub fn circuit_to_json<F>(chip_name: &str, op_name: &str, circuit: impl Circuit<F>)
where
F: FromUniformBytes<64> + Ord,
{
if F::MODULUS == Fq::MODULUS {
let model = circuit_model::<F, 48, 32>(&circuit);
update_json(chip_name, op_name, model).expect("csv generation failed");
}
}
#[cfg(target_os = "windows")]
pub fn circuit_to_json<F>(chip_name: &str, op_name: &str, circuit: impl Circuit<F>)
where
F: FromUniformBytes<64> + Ord,
{
}
static FILE_MUTEX: OnceLock<Arc<Mutex<()>>> = OnceLock::new();
fn get_file_mutex() -> &'static Arc<Mutex<()>> {
FILE_MUTEX.get_or_init(|| Arc::new(Mutex::new(())))
}
fn update_json(chip_name: &str, op_name: &str, model: CircuitModel) -> io::Result<()> {
let _lock = get_file_mutex().lock().expect("Failed to acquire mutex lock");
let file_path = "goldenfiles/cost-model.json";
let mut mint = Mint::new("goldenfiles");
let content = fs::read_to_string(file_path).unwrap_or("{}".to_string());
let mut json_value: Value = serde_json::from_str(&content).expect("Failed to parse JSON.");
if json_value.get(chip_name).is_none() {
json_value[chip_name] = json!({});
}
if json_value[chip_name].get(op_name).is_none() {
json_value[chip_name][op_name] = json!({});
}
report_model_json(&model, &mut json_value[chip_name][op_name])
.expect("Failed to construct JSON.");
json_value = sort_json(json_value);
let mut goldenfile =
mint.new_goldenfile("cost-model.json").expect("Failed to mint Goldenfile.");
writeln!(goldenfile, "{}", serde_json::to_string_pretty(&json_value)?).expect("7");
Ok(())
}
fn sort_json(value: Value) -> Value {
match value {
Value::Object(map) => {
let mut sorted_map = Map::new();
let mut keys: Vec<_> = map.keys().collect();
keys.sort();
for key in keys {
if let Some(val) = map.get(key) {
sorted_map.insert(key.clone(), sort_json(val.clone()));
}
}
Value::Object(sorted_map)
}
Value::Array(arr) => Value::Array(arr.into_iter().map(sort_json).collect()),
_ => value,
}
}
fn report_model_json(model: &CircuitModel, json_value: &mut Value) -> io::Result<()> {
let headers = [
"max_deg",
"rows",
"table_rows",
"advice_columns",
"fixed_columns",
"lookups",
"permutations",
"column_queries",
"point_sets",
];
let row = vec![
model.max_deg.to_string(),
model.rows.to_string(),
model.table_rows.to_string(),
model.advice_columns.to_string(),
model.fixed_columns.to_string(),
model.lookups.to_string(),
model.permutations.to_string(),
model.column_queries.to_string(),
model.point_sets.to_string(),
];
let mut map = BTreeMap::new();
for (key, value) in headers.iter().zip(row.into_iter()) {
map.insert(*key, value);
}
*json_value = serde_json::to_value(&map)?;
Ok(())
}