Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Discretized Grid

DiscretizedGrid discretizes a continuous d-dimensional domain and supports conversions between:

  • quantics indices
  • grid indices
  • original coordinates

Unlike the Julia package, the Rust API returns an error when original coordinates fall outside the configured domain.

Creating a one-dimensional grid

We can discretize the interval [0, 1) with R bits as follows:

use quanticsgrids::DiscretizedGrid;

fn main() -> quanticsgrids::Result<()> {
    let r = 4;
    let grid = DiscretizedGrid::builder(&[r])
        .with_bounds(0.0, 1.0)
        .build()?;

    let quantics = vec![0; r];
    let grididx = vec![0];

    assert_eq!(grid.quantics_to_grididx(&quantics)?, grididx);
    assert_eq!(grid.grididx_to_quantics(&grididx)?, quantics);
    assert!((grid.grididx_to_origcoord(&grididx)?[0] - 0.0).abs() < 1e-12);
    assert_eq!(grid.origcoord_to_grididx(&[0.0])?, grididx);

    let quantics = vec![1; r];
    let grididx = vec![2usize.pow(r as u32) - 1];
    let x = 1.0 - 1.0 / 2.0_f64.powi(r as i32);

    assert_eq!(grid.quantics_to_grididx(&quantics)?, grididx);
    assert_eq!(grid.grididx_to_quantics(&grididx)?, quantics);
    assert!((grid.quantics_to_origcoord(&quantics)?[0] - x).abs() < 1e-12);
    assert!((grid.grididx_to_origcoord(&grididx)?[0] - x).abs() < 1e-12);
    assert_eq!(grid.origcoord_to_grididx(&[x])?, grididx);
    assert_eq!(grid.origcoord_to_quantics(&[x])?, quantics);

    Ok(())
}

Supported resolutions

Coordinates are f64, so grid points must stay distinguishable: the rounding error of a conversion is about one ulp of the bounds, and a grid whose step falls below that limit cannot map coordinates to indices exactly. The limit therefore depends on the bounds, not on R alone:

BoundsHighest supported R
[0, 1)50
symmetric O(1), e.g. [-1.5, 1.5)51
[0.3, 10.3)50
[1e10, 1e10 + 1)17
[1e12, 1e12 + 1e6)30

build() returns QuanticsGridError::GridNotRepresentable above these, instead of producing a grid whose conversions silently return a neighbouring index. The check is conservative (step of at least four ulps of the bounds, plus the last two grid points checked), so it can also reject a grid that would happen to be exact. Within the accepted range, origcoord_to_grididx(&grididx_to_origcoord(&[g])?) returns g exactly.

Mapping coordinates to indices

A coordinate c maps to the index round((c - lower_bound) / grid_step), clamped to 0..=N-1; a normalized index exactly halfway between two indices rounds to the larger one. The rounding applies to that computed ratio, so the result is not promised to be the geometrically nearest emitted coordinate in every case.

The grid does not wrap around: in the half-open case a coordinate in the last step below the upper bound keeps the last grid point. Coordinates outside the domain ([lower, upper), or [lower, upper] with include_endpoint(true)) return QuanticsGridError::CoordinateOutOfBounds.

The Rust builder also supports including the upper endpoint:

use quanticsgrids::DiscretizedGrid;

fn main() -> quanticsgrids::Result<()> {
    let r = 4;
    let grid = DiscretizedGrid::builder(&[r])
        .with_bounds(0.0, 1.0)
        .include_endpoint(true)
        .build()?;

    assert!((grid.grididx_to_origcoord(&[0])?[0] - 0.0).abs() < 1e-12);
    assert!((grid.grididx_to_origcoord(&[2usize.pow(r as u32) - 1])?[0] - 1.0).abs() < 1e-12);

    Ok(())
}

Creating a d-dimensional grid

A d-dimensional grid is created in the same way by supplying one resolution per dimension. You can choose either fused or interleaved unfolding.

Fused representation

use quanticsgrids::{DiscretizedGrid, UnfoldingScheme};

fn main() -> quanticsgrids::Result<()> {
    let r = 4;
    let grid = DiscretizedGrid::builder(&[r, r, r])
        .with_lower_bound(&[0.0, 0.0, 0.0])
        .with_upper_bound(&[1.0, 1.0, 1.0])
        .with_unfolding_scheme(UnfoldingScheme::Fused)
        .build()?;

    let quantics = vec![0; r];
    let grididx = vec![0, 0, 0];

    assert_eq!(grid.quantics_to_grididx(&quantics)?, grididx);
    assert_eq!(grid.grididx_to_quantics(&grididx)?, quantics);
    assert_eq!(grid.origcoord_to_grididx(&[0.0, 0.0, 0.0])?, grididx);

    let quantics = vec![0, 0, 0, 1];
    let grididx = vec![1, 0, 0];

    assert_eq!(grid.quantics_to_grididx(&quantics)?, grididx);
    assert_eq!(grid.grididx_to_quantics(&grididx)?, quantics);

    let coord = grid.quantics_to_origcoord(&quantics)?;
    assert!((coord[0] - 1.0 / 16.0).abs() < 1e-12);
    assert!((coord[1] - 0.0).abs() < 1e-12);
    assert!((coord[2] - 0.0).abs() < 1e-12);

    Ok(())
}

Incrementing the least significant fused index increments the x coordinate first.

Interleaved representation

use quanticsgrids::{DiscretizedGrid, UnfoldingScheme};

fn main() -> quanticsgrids::Result<()> {
    let r = 4;
    let grid = DiscretizedGrid::builder(&[r, r, r])
        .with_lower_bound(&[0.0, 0.0, 0.0])
        .with_upper_bound(&[1.0, 1.0, 1.0])
        .with_unfolding_scheme(UnfoldingScheme::Interleaved)
        .build()?;

    let quantics = vec![0; 3 * r];
    let grididx = vec![0, 0, 0];

    assert_eq!(grid.quantics_to_grididx(&quantics)?, grididx);
    assert_eq!(grid.grididx_to_quantics(&grididx)?, quantics);
    assert_eq!(grid.origcoord_to_grididx(&[0.0, 0.0, 0.0])?, grididx);

    let quantics = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0];
    let grididx = vec![1, 0, 0];

    assert_eq!(grid.quantics_to_grididx(&quantics)?, grididx);
    assert_eq!(grid.grididx_to_quantics(&grididx)?, quantics);

    let coord = grid.quantics_to_origcoord(&quantics)?;
    assert!((coord[0] - 1.0 / 16.0).abs() < 1e-12);
    assert!((coord[1] - 0.0).abs() < 1e-12);
    assert!((coord[2] - 0.0).abs() < 1e-12);

    Ok(())
}