#![no_std]
#![forbid(rustdoc::broken_intra_doc_links)]
#![forbid(rustdoc::private_intra_doc_links)]
#![forbid(missing_docs)]
#![forbid(rustdoc::missing_crate_level_docs)]
#![forbid(rustdoc::private_doc_tests)]
#![forbid(rustdoc::invalid_codeblock_attributes)]
#![forbid(rustdoc::invalid_html_tags)]
#![forbid(rustdoc::invalid_rust_codeblocks)]
#![forbid(rustdoc::bare_urls)]
#![forbid(rustdoc::unescaped_backticks)]
#![forbid(rustdoc::redundant_explicit_links)]
#[cfg(any(feature = "debug1", feature = "std"))]
extern crate std;
mod compiler;
extern crate alloc;
use crate::compiler::Compiler;
use alloc::{
collections::{BTreeMap, BTreeSet},
vec::Vec,
};
use core::cell::RefCell;
use core::ops::Range;
#[cfg(feature = "std")]
pub use zyx_core::io::save;
use zyx_core::{
backend::Backend,
node::Node,
runtime::Runtime,
scalar::Scalar,
shape::Shape,
tensor::Id,
tensor::{tensor, IntoTensor},
};
pub use zyx_core::{dtype::DType, error::ZyxError, tensor::Tensor};
pub struct OpenCL(RefCell<Runtime<zyx_compiler::CompiledBackend<Compiler>>>);
pub fn device() -> Result<OpenCL, ZyxError> {
Ok(OpenCL(RefCell::new(Runtime::new(
zyx_compiler::CompiledBackend::new(Compiler::new(0, 8)?),
))))
}
pub fn device_builder() -> OpenCLBuilder {
OpenCLBuilder {
platform_id: 0,
queues_per_device: 8,
}
}
#[derive(Clone, Debug)]
pub struct OpenCLBuilder {
platform_id: usize,
queues_per_device: usize,
}
impl OpenCLBuilder {
pub fn platform_id(&mut self, platform_id: usize) -> Self {
self.platform_id = platform_id;
self.clone()
}
pub fn queues_per_device(&mut self, queues_per_device: usize) -> Self {
self.queues_per_device = queues_per_device;
self.clone()
}
pub fn build(self) -> Result<OpenCL, ZyxError> {
Ok(OpenCL(RefCell::new(Runtime::new(
zyx_compiler::CompiledBackend::new(Compiler::new(
self.platform_id,
self.queues_per_device,
)?),
))))
}
}
impl OpenCL {
#[must_use]
pub fn tensor<'a>(&'a self, data: impl IntoTensor<&'a Self>) -> Tensor<&'a Self> {
<&Self as Backend>::tensor(self, data).unwrap()
}
#[must_use]
pub fn randn(&self, shape: impl Into<Shape>, dtype: DType) -> Tensor<&Self> {
<&Self as Backend>::randn(self, shape, dtype).unwrap()
}
#[must_use]
pub fn uniform(&self, shape: impl Into<Shape>, range: Range<impl Scalar>) -> Tensor<&Self> {
<&Self as Backend>::uniform(self, shape, range).unwrap()
}
#[must_use]
pub fn full(&self, shape: impl Into<Shape>, value: impl Scalar) -> Tensor<&Self> {
<&Self as Backend>::full(self, shape, value).unwrap()
}
#[must_use]
pub fn zeros(&self, shape: impl Into<Shape>, dtype: DType) -> Tensor<&Self> {
<&Self as Backend>::zeros(self, shape, dtype).unwrap()
}
#[must_use]
pub fn ones(&self, shape: impl Into<Shape>, dtype: DType) -> Tensor<&Self> {
<&Self as Backend>::ones(self, shape, dtype).unwrap()
}
#[must_use]
pub fn eye(&self, n: usize, dtype: DType) -> Tensor<&Self> {
<&Self as Backend>::eye(self, n, dtype).unwrap()
}
#[cfg(feature = "std")]
pub fn load(
&self,
path: impl AsRef<std::path::Path>,
) -> Result<Vec<Tensor<&OpenCL>>, ZyxError> {
zyx_core::io::load(self, path)
}
#[must_use]
pub fn plot_graph<'a, B: Backend + 'a>(
&self,
tensors: impl IntoIterator<Item = &'a Tensor<B>>,
) -> alloc::string::String {
<&Self as Backend>::plot_graph(self, tensors)
}
#[allow(dead_code)]
fn debug_graph(&self) {
self.0.borrow_mut().debug_graph();
}
}
impl Backend for &OpenCL {
fn plot_graph<'a, B: Backend + 'a>(
self,
tensors: impl IntoIterator<Item = &'a Tensor<B>>,
) -> alloc::string::String {
let ids: Vec<Id> = tensors.into_iter().map(|t| t.id()).collect();
self.0.borrow().plot_graph_dot(&ids)
}
fn randn(self, shape: impl Into<Shape>, dtype: DType) -> Result<Tensor<Self>, ZyxError> {
Ok(tensor(
self.0.borrow_mut().randn(shape.into(), dtype)?,
self,
))
}
fn uniform(
self,
shape: impl Into<Shape>,
range: Range<impl Scalar>,
) -> Result<Tensor<Self>, ZyxError> {
Ok(tensor(
self.0.borrow_mut().uniform(shape.into(), range)?,
self,
))
}
fn shape(self, x: Id) -> Shape {
self.0.borrow().shape(x).clone()
}
fn dtype(self, x: Id) -> DType {
self.0.borrow().dtype(x)
}
fn backward(self, x: Id, sources: &BTreeSet<Id>) -> Result<BTreeMap<Id, Id>, ZyxError> {
self.0.borrow_mut().backward(x, sources)
}
fn load<T: Scalar>(self, x: Id) -> Result<Vec<T>, ZyxError> {
self.0.borrow_mut().load(x)
}
fn store<T: Scalar, IT>(self, iter: IT) -> Result<Id, ZyxError>
where
IT: IntoIterator<Item = T>,
IT::IntoIter: ExactSizeIterator,
{
self.0.borrow_mut().store(iter)
}
fn push(self, node: Node) -> Result<Id, ZyxError> {
self.0.borrow_mut().push(node)
}
fn release(self, x: Id) -> Result<(), ZyxError> {
self.0.borrow_mut().release(x)
}
fn retain(self, x: Id) {
self.0.borrow_mut().retain(x);
}
}
#[test]
fn dot_test2() -> Result<(), ZyxError> {
let dev = device_builder().platform_id(0).build()?;
let mut x = dev.randn([1024, 1024], DType::F32);
let begin = std::time::Instant::now();
for _ in 0..1000 {
x = x.dot(&x);
}
let _ = x.to_vec::<f32>();
let elapsed = begin.elapsed().as_millis();
std::println!("{elapsed}ms");
panic!();
Ok(())
}
#[test]
fn t6() {
let dev = device_builder().platform_id(0).build().unwrap();
let x = dev.randn([1024, 1024], DType::F32);
let z = x.sum(..);
let _: Vec<f32> = z.to_vec().unwrap();
}