Skip to main content

Program

Struct Program 

Source
pub struct Program {
    pub name: Option<String>,
    pub spins: usize,
    pub bias: Vec<(usize, f64)>,
    pub factors: Vec<Factor>,
    pub colors: Vec<Vec<usize>>,
    pub encodings: Vec<EncodedVar>,
    pub schedule: Schedule,
    pub observe: Vec<String>,
    pub target: Option<String>,
    pub price: Option<String>,
}
Expand description

A complete program.

Fields§

§name: Option<String>§spins: usize§bias: Vec<(usize, f64)>§factors: Vec<Factor>§colors: Vec<Vec<usize>>§encodings: Vec<EncodedVar>§schedule: Schedule§observe: Vec<String>§target: Option<String>§price: Option<String>

Implementations§

Source§

impl Program

Source

pub fn from_graph(g: &Graph, schedule: &Schedule) -> Program

Build a program from a graph and a schedule.

Source

pub fn to_graph(&self) -> Result<Graph, FtpError>

Rebuild a graph from this program. Factors of arity above two are refused here rather than silently dropped; lowering them to pairwise is a separate pass with its own ancillas.

Examples found in repository?
examples/hubo_vs_reduction.rs (line 139)
109fn main() {
110    let cases: [(usize, usize, usize); 4] = [(24, 3, 32), (32, 3, 48), (24, 4, 24), (40, 3, 60)];
111    let budgets: [usize; 6] = [1, 4, 16, 64, 256, 1024];
112
113    println!("hubo native vs Rosenberg reduction, on the same terms");
114    println!(
115        "mean best energy of the ORIGINAL model over {SEEDS} seeds; lower is better.\n\
116         The native arm runs once, at 1x. Every reduced column is a MULTIPLE of that same budget.\n"
117    );
118
119    print!(
120        "{:>4} {:>2} {:>4} {:>6} {:>4} {:>7} {:>8}  ",
121        "n", "k", "trm", "spins", "anc", "pen/w", "native"
122    );
123    for b in budgets {
124        print!("{:>9}", format!("red {b}x"));
125    }
126    println!("   broken");
127
128    for (n, k, t) in cases {
129        let mut native = 0.0f64;
130        let mut reduced = [0.0f64; 6];
131        let mut broken = 0usize;
132        let (mut ancillas, mut rspins, mut penalty) = (0usize, 0usize, 0.0f64);
133
134        for seed in 0..SEEDS {
135            let terms = instance(n, k, t, seed);
136            let h = hubo_of(&terms, n);
137            let prog = Program::from_ftp(&ftp_of(&terms, n)).expect("a well-formed program");
138            let red = reduce::to_pairwise(&prog).expect("a reducible program");
139            let g = red.program.to_graph().expect("a pairwise graph");
140            ancillas = red.ancillas;
141            rspins = red.program.spins;
142            penalty = red.penalty;
143
144            let p = hubo::Params {
145                beta_min: 0.05,
146                beta_max: 8.0,
147                stages: STAGES,
148                sweeps_per_stage: SWEEPS,
149            };
150            native += hubo::anneal(&h, &p, seed).energy;
151
152            let ladder = geometric_ladder(REDUCED_BETA_MIN, 8.0, STAGES);
153            for (i, mult) in budgets.iter().enumerate() {
154                let sched: Vec<(f64, usize)> =
155                    ladder.iter().map(|&b| (b, SWEEPS * mult)).collect();
156                let (state, reduced_e) = tempering::anneal(&g, &sched, seed, None);
157                let original_e = h.energy(&state[..n]);
158                reduced[i] += original_e;
159                // Comparing the two energies IS the ancilla check, and it needs no knowledge of
160                // which spins are ancillas or of how they were defined.
161                if ((reduced_e + red.offset) - original_e).abs() > 1e-6 {
162                    broken += 1;
163                }
164            }
165        }
166
167        let m = SEEDS as f64;
168        print!(
169            "{n:>4} {k:>2} {t:>4} {rspins:>6} {ancillas:>4} {:>7.0} {:>8.2}  ",
170            penalty,
171            native / m
172        );
173        for r in reduced {
174            print!("{:>9.2}", r / m);
175        }
176        println!("   {:>3}/{}", broken, SEEDS as usize * budgets.len());
177    }
178
179    println!(
180        "\n'anc' is the ancillas the reduction added and 'spins' the graph it had to search: each \
181         one is a variable\nthe answer depends on and the question never mentioned. The native path \
182         adds none.\n\n\
183         'pen/w' is the penalty the reduction chose, against term weights of 1. That ratio is the \
184         mechanism: any\nsingle flip that would move the search must first pay it, so the landscape \
185         is rigid and a single-flip\nsampler cannot traverse it. 'broken' counts runs whose ancillas \
186         did not hold, and it stays at zero --\nwhich is the confirmation, not a null result: the \
187         reduced arm is stuck inside the feasible region\nrather than wandering out of it.\n\n\
188         The budget columns are the finding. If the reduced arm caught up at 64x or 256x, the cost \
189         of quadratising\nwould be compute, and compute is buyable."
190    );
191}
Source

pub fn to_ftp(&self) -> String

Serialise. The output re-parses to an equal program and re-writes byte-identically.

Source

pub fn from_ftp(text: &str) -> Result<Program, FtpError>

Parse. Errors carry the line number, because a format nobody can debug is a format nobody adopts.

Examples found in repository?
examples/hubo_vs_reduction.rs (line 137)
109fn main() {
110    let cases: [(usize, usize, usize); 4] = [(24, 3, 32), (32, 3, 48), (24, 4, 24), (40, 3, 60)];
111    let budgets: [usize; 6] = [1, 4, 16, 64, 256, 1024];
112
113    println!("hubo native vs Rosenberg reduction, on the same terms");
114    println!(
115        "mean best energy of the ORIGINAL model over {SEEDS} seeds; lower is better.\n\
116         The native arm runs once, at 1x. Every reduced column is a MULTIPLE of that same budget.\n"
117    );
118
119    print!(
120        "{:>4} {:>2} {:>4} {:>6} {:>4} {:>7} {:>8}  ",
121        "n", "k", "trm", "spins", "anc", "pen/w", "native"
122    );
123    for b in budgets {
124        print!("{:>9}", format!("red {b}x"));
125    }
126    println!("   broken");
127
128    for (n, k, t) in cases {
129        let mut native = 0.0f64;
130        let mut reduced = [0.0f64; 6];
131        let mut broken = 0usize;
132        let (mut ancillas, mut rspins, mut penalty) = (0usize, 0usize, 0.0f64);
133
134        for seed in 0..SEEDS {
135            let terms = instance(n, k, t, seed);
136            let h = hubo_of(&terms, n);
137            let prog = Program::from_ftp(&ftp_of(&terms, n)).expect("a well-formed program");
138            let red = reduce::to_pairwise(&prog).expect("a reducible program");
139            let g = red.program.to_graph().expect("a pairwise graph");
140            ancillas = red.ancillas;
141            rspins = red.program.spins;
142            penalty = red.penalty;
143
144            let p = hubo::Params {
145                beta_min: 0.05,
146                beta_max: 8.0,
147                stages: STAGES,
148                sweeps_per_stage: SWEEPS,
149            };
150            native += hubo::anneal(&h, &p, seed).energy;
151
152            let ladder = geometric_ladder(REDUCED_BETA_MIN, 8.0, STAGES);
153            for (i, mult) in budgets.iter().enumerate() {
154                let sched: Vec<(f64, usize)> =
155                    ladder.iter().map(|&b| (b, SWEEPS * mult)).collect();
156                let (state, reduced_e) = tempering::anneal(&g, &sched, seed, None);
157                let original_e = h.energy(&state[..n]);
158                reduced[i] += original_e;
159                // Comparing the two energies IS the ancilla check, and it needs no knowledge of
160                // which spins are ancillas or of how they were defined.
161                if ((reduced_e + red.offset) - original_e).abs() > 1e-6 {
162                    broken += 1;
163                }
164            }
165        }
166
167        let m = SEEDS as f64;
168        print!(
169            "{n:>4} {k:>2} {t:>4} {rspins:>6} {ancillas:>4} {:>7.0} {:>8.2}  ",
170            penalty,
171            native / m
172        );
173        for r in reduced {
174            print!("{:>9.2}", r / m);
175        }
176        println!("   {:>3}/{}", broken, SEEDS as usize * budgets.len());
177    }
178
179    println!(
180        "\n'anc' is the ancillas the reduction added and 'spins' the graph it had to search: each \
181         one is a variable\nthe answer depends on and the question never mentioned. The native path \
182         adds none.\n\n\
183         'pen/w' is the penalty the reduction chose, against term weights of 1. That ratio is the \
184         mechanism: any\nsingle flip that would move the search must first pay it, so the landscape \
185         is rigid and a single-flip\nsampler cannot traverse it. 'broken' counts runs whose ancillas \
186         did not hold, and it stays at zero --\nwhich is the confirmation, not a null result: the \
187         reduced arm is stuck inside the feasible region\nrather than wandering out of it.\n\n\
188         The budget columns are the finding. If the reduced arm caught up at 64x or 256x, the cost \
189         of quadratising\nwould be compute, and compute is buyable."
190    );
191}
Source

pub fn digest(&self) -> u64

A stable digest of the canonical text, for asserting that two runs ran the same program.

FNV-1a over the serialisation. Not a security hash and not claimed to be one; it answers “is this the same program” and nothing else.

Trait Implementations§

Source§

impl Clone for Program

Source§

fn clone(&self) -> Program

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Program

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Program

Source§

fn default() -> Program

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Program

Source§

fn eq(&self, other: &Program) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Program

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.