qualia_core_db/services/swarm/
job.rs1use crate::solvers::learning::kg_embedding::{EmbeddingTable, TrainConfig};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum JobKind {
12 DenseLinearProduct,
15 EmbeddingArtifact,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum JobMode {
23 Personal,
25 Collaborative { peers: Vec<u64> },
27 Paid {
29 requester_did: u64,
30 provider_did: u64,
31 price_micro_units: u64,
33 },
34}
35
36#[derive(Debug, Clone, PartialEq)]
39pub enum JobInput {
40 DenseLinearProduct {
42 m: usize,
43 k: usize,
44 n: usize,
45 a: Vec<f64>,
46 b: Vec<f64>,
47 },
48 EmbeddingArtifact {
51 triples: Vec<(usize, usize, usize)>,
52 n_entities: usize,
53 n_relations: usize,
54 cfg: TrainConfig,
55 check: Vec<(usize, usize, usize)>,
56 },
57}
58
59impl JobInput {
60 pub fn kind(&self) -> JobKind {
61 match self {
62 JobInput::DenseLinearProduct { .. } => JobKind::DenseLinearProduct,
63 JobInput::EmbeddingArtifact { .. } => JobKind::EmbeddingArtifact,
64 }
65 }
66
67 pub fn is_well_formed(&self) -> bool {
69 match self {
70 JobInput::DenseLinearProduct { m, k, n, a, b } => {
71 *m > 0 && *k > 0 && *n > 0 && a.len() == m * k && b.len() == k * n
72 }
73 JobInput::EmbeddingArtifact {
74 triples,
75 n_entities,
76 n_relations,
77 ..
78 } => {
79 !triples.is_empty()
80 && *n_entities > 0
81 && *n_relations > 0
82 && triples
83 .iter()
84 .all(|&(h, r, t)| h < *n_entities && t < *n_entities && r < *n_relations)
85 }
86 }
87 }
88}
89
90#[derive(Debug, Clone, PartialEq)]
92pub enum JobResult {
93 DenseLinearProduct { c: Vec<f64> },
94 EmbeddingArtifact { table: EmbeddingTable },
95}
96
97impl JobResult {
98 pub fn kind(&self) -> JobKind {
99 match self {
100 JobResult::DenseLinearProduct { .. } => JobKind::DenseLinearProduct,
101 JobResult::EmbeddingArtifact { .. } => JobKind::EmbeddingArtifact,
102 }
103 }
104}
105
106#[derive(Debug, Clone, PartialEq)]
108pub struct JobSpec {
109 pub id: u64,
111 pub mode: JobMode,
112 pub input: JobInput,
113}
114
115impl JobSpec {
116 pub fn new(mode: JobMode, input: JobInput) -> Self {
117 let id = content_id(&input);
118 Self { id, mode, input }
119 }
120
121 pub fn kind(&self) -> JobKind {
122 self.input.kind()
123 }
124}
125
126pub fn content_id(input: &JobInput) -> u64 {
129 let mut h: u64 = 0xcbf29ce484222325;
130 let byte = |b: u8, h: &mut u64| {
131 *h ^= b as u64;
132 *h = h.wrapping_mul(0x100000001b3);
133 };
134 let word = |w: u64, h: &mut u64| {
135 for b in w.to_le_bytes() {
136 byte(b, h);
137 }
138 };
139 match input {
140 JobInput::DenseLinearProduct { m, k, n, a, b } => {
141 byte(0x01, &mut h);
142 word(*m as u64, &mut h);
143 word(*k as u64, &mut h);
144 word(*n as u64, &mut h);
145 for &v in a.iter().chain(b.iter()) {
146 word(v.to_bits(), &mut h);
147 }
148 }
149 JobInput::EmbeddingArtifact {
150 triples,
151 n_entities,
152 n_relations,
153 cfg,
154 check,
155 } => {
156 byte(0x02, &mut h);
157 word(*n_entities as u64, &mut h);
158 word(*n_relations as u64, &mut h);
159 word(cfg.seed, &mut h);
160 word(cfg.rank as u64, &mut h);
161 word(cfg.epochs as u64, &mut h);
162 for &(a, b, c) in triples.iter().chain(check.iter()) {
163 word(a as u64, &mut h);
164 word(b as u64, &mut h);
165 word(c as u64, &mut h);
166 }
167 }
168 }
169 h
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 fn dense() -> JobInput {
177 JobInput::DenseLinearProduct {
178 m: 2,
179 k: 2,
180 n: 2,
181 a: vec![1.0, 2.0, 3.0, 4.0],
182 b: vec![1.0, 0.0, 0.0, 1.0],
183 }
184 }
185
186 #[test]
187 fn content_id_is_deterministic_and_input_sensitive() {
188 let a = content_id(&dense());
189 let b = content_id(&dense());
190 assert_eq!(a, b, "same input → same id");
191 let mut other = dense();
192 if let JobInput::DenseLinearProduct { a, .. } = &mut other {
193 a[0] = 9.0;
194 }
195 assert_ne!(content_id(&other), a, "different input → different id");
196 }
197
198 #[test]
199 fn well_formedness_catches_bad_dims() {
200 assert!(dense().is_well_formed());
201 let bad = JobInput::DenseLinearProduct {
202 m: 2,
203 k: 2,
204 n: 2,
205 a: vec![1.0],
206 b: vec![1.0; 4],
207 };
208 assert!(!bad.is_well_formed());
209 }
210
211 #[test]
212 fn spec_carries_kind_and_id() {
213 let spec = JobSpec::new(JobMode::Personal, dense());
214 assert_eq!(spec.kind(), JobKind::DenseLinearProduct);
215 assert_eq!(spec.id, content_id(&dense()));
216 }
217}