-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Function chain #51
base: master
Are you sure you want to change the base?
Conversation
The usage is showed in test let mut b = LocalBuilder::default();
let ra = b.try_register(3).unwrap();
let rb = b.try_register(3).unwrap();
let [ra, rb] = Circuit::default()
// Applies gamma to |ra>|rb>
.apply(gamma, [0, 1])
// Applies gamma to |ra[0] ra[1]>|ra[2]>
.under_new_indices(
[&[(0, 0), (0, 1)], &[(0, 2)]],
Circuit::default().apply(gamma, [0, 1]),
)
.input([ra, rb])
.run(&mut b)?; I have just finished |
I like the idea -- I'd prefer to keep the I'm not sure about the Maybe we could support a function to remap the indices, so the user may define
Here Just a thought -- any ideas? |
To be honest, I also think my I'm considering also supporting something like |
8fa9c91
to
5b5fe2d
Compare
d251009
to
30b4adb
Compare
Now, with trait let [ra, rb] = Circuit::default()
// Applies gamma to |ra>|rb>
.apply(gamma, [0, 1])
// Applies gamma to |ra[0] ra[1]>|ra[2]>
.apply(gamma, |[ra, _]: Idx<2>| {
[ra[0..=1].to_vec(), vec![ra[2]]]
})
// Applies gamma to |ra[0] rb[0]>|ra[2]>
.apply(gamma, |[ra, rb]: Idx<2>| {
[vec![ra[0], rb[0]], vec![ra[2]]]
})
// Applies gamma to |ra[0]>|rb[0] ra[2]>
.apply(gamma, |[ra, rb]: Idx<2>| {
[vec![ra[0]], vec![rb[0], ra[2]]]
})
// Applies a more complex subcircuit to |ra[1]>|ra[2]>|rb>
.apply(
Circuit::default()
.apply(gamma, [0, 1])
.apply(gamma, [1, 2])
.apply(
|b: &mut CurrentBuilderType, rs| {
let [ra, rb] = rs;
let (ra, rb) = b.cnot(ra, rb)?;
Ok([ra, rb])
},
|[_, r2, r3]: Idx<3>| [r2, vec![r3[0]]],
),
|[ra, rb]: Idx<2>| [vec![ra[1]], vec![ra[2]], rb],
)
.input([ra, rb])
.run(&mut b)?; |
dc36afe
to
62bf998
Compare
Yeah this version is much more readable to me - only other note: is it possible to use some Into traits on the closure signature so it can return arrays, vectors, or handles? We can also just push a simple version and add that afterwards. |
The bad news is that rust doesn't support generics for closures. Maybe we can put generics into the I'm facing another problem. I worry that my Would it be possible to split Additionally, is the builder trait doing too much? How about only providing Also, I'm considering rewriting |
I see, the issue you're running into is that you'd like the internal structure of the Circuit to be a single function which contains the whole function chain? You may have to structure it as a vector of enums which representing a single function application or conditioned application? As for the generics, I'm not sure I understand. I was thinking of using the Into trait heavily. Something like the following: #[derive(Default)]
struct Circuit<const N: usize> {
pipeline: Vec<IndexMask<N>>,
}
impl<const N: usize> Circuit<N> {
fn index<F, RH>(&mut self, indices: F) -> &mut Self where F: Fn([RegisterHandle; N]) -> RH + 'static, RH: Into<RegisterHandle> {
self.index_mask(indices)
}
fn index_mask<MI>(&mut self, indices: MI) -> &mut Self where MI: Into<IndexMask<N>> {
let indices = indices.into();
self.pipeline.push(indices);
self
}
}
struct RegisterHandle {
registers: Vec<(usize, usize)>,
}
enum IndexMask<const N: usize> {
Function(Box<dyn Fn([RegisterHandle; N]) -> RegisterHandle>)
}
impl<const N: usize, F, RH> From<F> for IndexMask<N> where F: Fn([RegisterHandle; N]) -> RH + 'static, RH: Into<RegisterHandle> {
fn from(value: F) -> Self {
IndexMask::Function(Box::new(move |x| -> RegisterHandle {
value(x).into()
}))
}
}
impl<RHI, RH> From<RHI> for RegisterHandle where RHI: IntoIterator<Item=RH>, RH: Into<RegisterHandle> {
fn from(value: RHI) -> Self {
RegisterHandle {
registers: value.into_iter().flat_map(|x| x.into().registers).collect(),
}
}
} This allows us to write code such as: fn test_remap() {
Circuit::default().index(|[a, b, c]| {
[a, b]
}).index(|[a, b, c]| {
vec![a, b, c]
}).index(|[a, b, c]| {
[vec![a, b], vec![c]]
}).index(|[a, b, c]| a);
} Notably the return type can be whatever is convenient/readable at the time |
This is the beginning of the plan to rewrite program macro to the function chain.
The program macro seems not necessary, as rust's type system should work fine with the process of applying a gate / sub-circuit to some qubits.
I see that the plan of rewriting program macro was also mentioned in #39