packages feed

tensorflow (empty) → 0.1.0.0

raw patch · 18 files changed

+2888/−0 lines, 18 filesdep +HUnitdep +asyncdep +attoparsecsetup-changed

Dependencies added: HUnit, async, attoparsec, base, bytestring, containers, data-default, exceptions, fgl, lens-family, mainland-pretty, mtl, proto-lens, proto-lens-protoc, semigroups, split, temporary, tensorflow, tensorflow-proto, test-framework, test-framework-hunit, test-framework-quickcheck2, text, transformers, vector

Files

+ LICENSE view
@@ -0,0 +1,203 @@+Copyright 2016 The TensorFlow Authors.  All rights reserved.++                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright 2016, The TensorFlow Authors.++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple++main = defaultMain
+ src/TensorFlow/Build.hs view
@@ -0,0 +1,339 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeFamilies #-}+module TensorFlow.Build+    ( -- * Graph node types+      ControlNode(..)+    , Unique+    -- * Ops+    , explicitName+    , implicitName+    , opDef+    , opDefWithName+    , opName+    , opType+    , opAttr+    , opInputs+    , opControlInputs+    -- * The Build monad+    , GraphState+    , renderedNodeDefs+    , BuildT+    , Build+    , MonadBuild(..)+    , addInitializer+    , hoistBuildT+    , evalBuildT+    , runBuildT+    , asGraphDef+    , addGraphDef+    , flushInitializers+    , flushNodeBuffer+    , summaries+    -- * Creating and looking up Ops+    , getOrAddOp+    , addNewOp+    , encodeOutput+    , lookupNode+    -- * Modifying all nodes in a Build action+    , withStateLens+    , withDevice+    , withNameScope+    , withNodeDependencies+    ) where++import Control.Monad.Catch (MonadThrow, MonadCatch, MonadMask)+import Control.Monad.Fix (MonadFix(..))+import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad.Trans.Class (MonadTrans(..))+import Control.Monad.Trans.State.Strict(StateT(..), mapStateT, evalStateT)+import Data.Default (def)+import Data.Functor.Identity (Identity(..))+import qualified Data.Map.Strict as Map+import Data.Monoid ((<>))+import qualified Data.Set as Set+import Data.Set (Set)+import Data.String (IsString(..))+import Data.Text (Text)+import qualified Data.Text as Text+import Lens.Family2 (Lens', (.~), (^.), (&))+import Lens.Family2.State.Strict (MonadState, use, uses, (.=), (<>=), (%=))+import Lens.Family2.Unchecked (lens)+import Proto.Tensorflow.Core.Framework.Graph+    ( GraphDef+    , node+    )+import Proto.Tensorflow.Core.Framework.NodeDef+    ( NodeDef+    , attr+    , input+    , device+    , name+    , op+    )++import TensorFlow.Orphans ()+import TensorFlow.Output++newtype Unique = Unique Int+    deriving (Eq, Ord, Enum)++--------------++implicitName :: PendingNodeName+implicitName = ImplicitName++explicitName :: Text -> PendingNodeName+explicitName = ExplicitName++newtype Scope = Scope {unScope :: Text}+    deriving (Eq, Ord, IsString)++instance Show Scope where+    show = show . unScope++opDef :: OpType -> OpDef+opDef = opDefWithName ImplicitName++opDefWithName :: PendingNodeName -> OpType -> OpDef+opDefWithName n t = OpDef+    { _opName = n+    , _opType = t+    , _opAttrs = Map.empty+    , _opInputs = []+    , _opControlInputs = []+    }++data GraphState = GraphState+    { _renderedNodes :: !(Map.Map PendingNode NodeDef)+        -- ^ Nodes which have been rendered.  Keeps track of the unique ID we+        -- assign each implicitly-named node.  Also prevents us from adding the+        -- same node (implicit or explicit) more than once to the nodeBuffer.+    , _renderedNodeDefs :: !(Map.Map NodeName NodeDef)+        -- ^ The NodeDefs of nodes which have been rendered. Used by the+        -- Gradient module to inspect the node graph.+    , _nodeBuffer :: [NodeDef]+        -- ^ A list of nodes that should be passed to TensorFlow during+        -- the next call to Session.extend (TF_ExtendGraph).+    , _nextUnique :: !Unique+        -- ^ Unique ID for the next node+    -- TODO(judahjacobson): watch for clashes between auto and user names.+    , _defaultDevice :: !(Maybe Device)+    , _currentScope :: [Scope]+    , _defaultControlInputs :: !(Set NodeName)+    , _initializationNodes  :: [NodeName]+      -- ^ The nodes to run next time a TF.run is issued, typically+      -- variable initializers.+    , _summaries :: [Output]+      -- ^ The tensors for summary (ByteString type)+    }++-- | A node definition without its final name.  Used as a key in the+-- "renderedNodes" map.+-- The NodeDef contained inside has an empty "name" field.+data PendingNode = PendingNode [Scope] !PendingNodeName !NodeDef+    deriving (Eq, Ord)++-- Returns an _incomplete_ NodeDef. The name is fixed by addNewOpFromPending.+pendingNodeDef :: PendingNode -> NodeDef+pendingNodeDef (PendingNode _ _ n) = n++initGraphState :: GraphState+initGraphState =+    GraphState Map.empty Map.empty [] (Unique 0) Nothing [] Set.empty [] []++renderedNodes :: Lens' GraphState (Map.Map PendingNode NodeDef)+renderedNodes = lens _renderedNodes (\g x -> g { _renderedNodes = x })++renderedNodeDefs :: Lens' GraphState (Map.Map NodeName NodeDef)+renderedNodeDefs = lens _renderedNodeDefs (\g x -> g { _renderedNodeDefs = x })++nodeBuffer :: Lens' GraphState [NodeDef]+nodeBuffer = lens _nodeBuffer (\g x -> g { _nodeBuffer = x })++nextUnique :: Lens' GraphState Unique+nextUnique = lens _nextUnique (\g x -> g { _nextUnique = x })++defaultDevice :: Lens' GraphState (Maybe Device)+defaultDevice = lens _defaultDevice (\g x -> g { _defaultDevice = x })++currentScope :: Lens' GraphState [Scope]+currentScope = lens _currentScope (\g x -> g { _currentScope = x })++defaultControlInputs :: Lens' GraphState (Set NodeName)+defaultControlInputs = lens _defaultControlInputs+                          (\g x -> g { _defaultControlInputs = x })++initializationNodes :: Lens' GraphState [NodeName]+initializationNodes = lens _initializationNodes (\g x -> g { _initializationNodes = x })++summaries :: Lens' GraphState [Output]+summaries = lens _summaries (\g x -> g { _summaries = x })++-- | An action for building nodes in a TensorFlow graph.+-- Used to manage build state internally as part of the @Session@ monad.+newtype BuildT m a = BuildT (StateT GraphState m a)+    deriving (Functor, Applicative, Monad, MonadIO, MonadTrans,+              MonadState GraphState, MonadThrow, MonadCatch, MonadMask,+              MonadFix)++-- | An action for building nodes in a TensorFlow graph.+type Build = BuildT Identity++-- | This is Control.Monad.Morph.hoist sans the dependency.+hoistBuildT :: (forall a . m a -> n a) -> BuildT m b -> BuildT n b+hoistBuildT f (BuildT m) = BuildT $ mapStateT f m++runBuildT :: BuildT m a -> m (a, GraphState)+runBuildT (BuildT f) = runStateT f initGraphState++evalBuildT :: Monad m => BuildT m a -> m a+evalBuildT (BuildT f) = evalStateT f initGraphState++-- | Lift a 'Build' action into a monad, including any explicit op renderings.+class Monad m => MonadBuild m where+    build :: Build a -> m a++instance Monad m => MonadBuild (BuildT m) where+    build = hoistBuildT $ return . runIdentity++-- | Get all the NodeDefs that have accumulated so far, and clear that buffer.+flushNodeBuffer :: MonadBuild m => m [NodeDef]+flushNodeBuffer = build $ do+    ns <- use nodeBuffer+    nodeBuffer .= []+    return ns++-- | Get all the initializers that have accumulated so far, and clear+-- that buffer.+flushInitializers :: Monad m => BuildT m [NodeName]+flushInitializers = do+    ns <- use initializationNodes+    initializationNodes .= []+    return ns++-- | Registers the given node to be executed before the next+-- 'TensorFlow.Session.run'.+addInitializer :: MonadBuild m => ControlNode -> m ()+addInitializer (ControlNode i) = build $ initializationNodes %= (i:)++-- | Produce a GraphDef proto representation of the nodes that are rendered in+-- the given 'Build' action.+asGraphDef :: Build a -> GraphDef+asGraphDef b = def & node .~ gs ^. nodeBuffer+  where+    gs = snd $ runIdentity $ runBuildT b++-- TODO: check against existing nodes for conflicts?+addGraphDef :: MonadBuild m => GraphDef -> m ()+addGraphDef g = build $ nodeBuffer <>= g ^. node++-- | Render the given op if it hasn't been rendered already, and return its+-- name.+getOrAddOp :: OpDef -> Build NodeName+getOrAddOp o = do+    pending <- getPendingNode o+    uses renderedNodes (Map.lookup pending) >>= \case+        Just n -> return $ NodeName $ n ^. name+        Nothing -> addNewOpFromPending pending++lookupNode :: NodeName -> Build NodeDef+lookupNode n = uses renderedNodeDefs (Map.lookup n) >>= \case+    Just n' -> return n'+    Nothing -> error $ "lookupNode: unknown node name " ++ show n++-- | Add a new node for a given 'OpDef'.  This is used for making "stateful" ops+-- which are not safe to dedup (e.g, "variable" and "assign").+addNewOp :: OpDef -> Build NodeName+addNewOp o = getPendingNode o >>= addNewOpFromPending++addNewOpFromPending :: PendingNode -> Build NodeName+addNewOpFromPending pending = do+    nodeName <- renderPendingNode pending+    let nodeDef = pendingNodeDef pending & name .~ unNodeName nodeName+    nodeBuffer %= (nodeDef :)+    renderedNodes %= Map.insert pending nodeDef+    renderedNodeDefs %= Map.insert nodeName nodeDef+    return nodeName++-- | Get the pending node corresponding to an OpDef, which may or may not have+-- been rendered before.  Implicitly renders all of this node's inputs.+getPendingNode :: OpDef -> Build PendingNode+getPendingNode o = do+    -- An empty string in the proto field means that no specific+    -- device is specified.+    dev <- maybe "" deviceName <$> use defaultDevice+    scope <- use currentScope+    controls <- use defaultControlInputs+    let inputs = map encodeOutput (o ^. opInputs)+    let controlInputs+            = map makeDep (o ^. opControlInputs ++ Set.toList controls)+    return $ PendingNode scope (o ^. opName)+            $ def & op .~ (unOpType (o ^. opType) :: Text)+                  & attr .~ _opAttrs o+                  & input .~ (inputs ++ controlInputs)+                  & device .~ dev+  where+    makeDep = ("^" <>) . unNodeName++-- | Pick a name for a pending node.  If it has an explicit name, just use that;+-- if the name is implicit, assign a new unique name based on the op type.+renderPendingNode :: PendingNode -> Build NodeName+renderPendingNode (PendingNode scope pendingName nodeDef)+    = NodeName . (scopePrefix <>) <$> getName+  where+    scopePrefix = Text.concat $ fmap ((<> "/") . unScope) scope+    getName = case pendingName of+        ExplicitName n -> return n+        ImplicitName -> do+            u@(Unique k) <- use nextUnique+            nextUnique .= succ u+            return $ nodeDef ^. op <> "_" <> Text.pack (show k)++-- | Turn an 'Output' into a string representation for the TensorFlow+-- foreign APIs.+encodeOutput :: Output -> Text+encodeOutput (Output (OutputIx 0) n) = unNodeName n+encodeOutput (Output (OutputIx i) n) = unNodeName n <> Text.pack (':' : show i)++-- | Modify some part of the state, run an action, and restore the state+-- after that action is done.+withStateLens :: MonadBuild m => Lens' GraphState a -> (a -> a) -> m b -> m b+withStateLens accessor f act = do+    old <- build $ use accessor+    build $ accessor %= f+    result <- act+    build $ accessor .= old+    return result++-- | Set a device for all nodes rendered in the given 'Build' action+-- (unless further overridden by another use of withDevice).+withDevice :: MonadBuild m => Maybe Device -> m a -> m a+withDevice d = withStateLens defaultDevice (const d)++-- | Prepend a scope to all nodes rendered in the given 'Build' action.+withNameScope :: MonadBuild m => Text -> m a -> m a+withNameScope s = withStateLens currentScope (Scope s :)++-- | Add control inputs to all nodes rendered in the given 'Build' action.+withNodeDependencies :: MonadBuild m => Set NodeName -> m a -> m a+withNodeDependencies nodes = withStateLens defaultControlInputs (<> nodes)
+ src/TensorFlow/BuildOp.hs view
@@ -0,0 +1,306 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-}++module TensorFlow.BuildOp+    ( BuildResult(..)+    , buildOp+    , PureResult(..)+    , pureOp+    , eqLengthGuard+    , BuildInputs(..)+    , OpParams+    )+  where++import Control.Monad (liftM2, replicateM)+import Control.Monad.Reader (ReaderT, runReaderT, ask)+import Control.Monad.State.Strict (State, evalState, get, put)+import Data.Int (Int64)++import TensorFlow.Build+import TensorFlow.Output+import TensorFlow.Tensor+import TensorFlow.Types++data ResultState = ResultState !OutputIx [Int64] deriving Show++type Result = ReaderT NodeName (State ResultState)++-- | Class of types that can be used as op outputs.+class BuildResult a where+    buildResult :: Result a++instance (BuildResult a1, BuildResult a2) => BuildResult (a1, a2) where+    buildResult = (,) <$> buildResult <*> buildResult++instance (BuildResult a1, BuildResult a2, BuildResult a3) => BuildResult (a1, a2, a3) where+    buildResult = (,,) <$> buildResult <*> buildResult <*> buildResult++instance (BuildResult a1, BuildResult a2, BuildResult a3, BuildResult a4)+         => BuildResult (a1, a2, a3, a4) where+    buildResult = (,,,) <$> buildResult <*> buildResult <*> buildResult <*> buildResult++instance (BuildResult a1, BuildResult a2, BuildResult a3, BuildResult a4, BuildResult a5)+         => BuildResult (a1, a2, a3, a4, a5) where+    buildResult = (,,,,) <$> buildResult+                      <*> buildResult+                      <*> buildResult+                      <*> buildResult+                      <*> buildResult++instance ( BuildResult a1+         , BuildResult a2+         , BuildResult a3+         , BuildResult a4+         , BuildResult a5+         , BuildResult a6+         )+         => BuildResult (a1, a2, a3, a4, a5, a6) where+    buildResult = (,,,,,)+               <$> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult++instance ( BuildResult a1+         , BuildResult a2+         , BuildResult a3+         , BuildResult a4+         , BuildResult a5+         , BuildResult a6+         , BuildResult a7+         )+         => BuildResult (a1, a2, a3, a4, a5, a6, a7) where+    buildResult = (,,,,,,)+               <$> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult++instance ( BuildResult a1+         , BuildResult a2+         , BuildResult a3+         , BuildResult a4+         , BuildResult a5+         , BuildResult a6+         , BuildResult a7+         , BuildResult a8+         )+         => BuildResult (a1, a2, a3, a4, a5, a6, a7, a8) where+    buildResult = (,,,,,,,)+               <$> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult+               <*> buildResult++recordResult :: Result Output+recordResult = do+    o <- ask+    ResultState i ns <- get+    put $! ResultState (i+1) ns+    return $! output i o++instance Rendered v => BuildResult (Tensor v a) where+    buildResult = Tensor . pure <$> recordResult++instance BuildResult ControlNode where+    buildResult = ControlNode <$> ask++instance (Rendered v, TensorTypes as) => BuildResult (TensorList v as) where+  buildResult = loop (tensorTypes :: TensorTypeList as)+    where+        loop :: TensorTypeList bs -> Result (TensorList v bs)+        loop Nil = return Nil+        loop (TensorTypeProxy :/ ls) = do+            t <- buildResult+            ts <- loop ls+            return (t :/ ts)++instance BuildResult a => BuildResult [a] where+    buildResult = do+        ResultState i ns <- get+        case ns of+            [] -> error $ "Ran out of counts in buildResult. " +++                          "Likely misuse of buildOp."+            (n : rest) -> do+                put $! ResultState i rest+                replicateM (fromIntegral n) buildResult++buildOp :: BuildResult a => [Int64] -> OpDef -> Build a+buildOp sizes o = do+    n <- addNewOp o+    return $ flip evalState (ResultState 0 sizes) (runReaderT buildResult n)++-- | Returns true if all the integers in each tuple are identical.+-- Throws an error with a descriptive message if not.+eqLengthGuard :: [(String, [(String, Int)])] -> Bool+eqLengthGuard = all eachOk+  where+    eachOk (_, []) = True+    -- The next line has (== 1) . length . nub in disguise+    eachOk (numberAttrName, pairs@((_, x) : zs)) = all (\z -> snd z == x) zs ||+        error ("number_attr " ++ numberAttrName +++               " contains tensors with different length " ++ show pairs)++-----------+++-- | Class of types that can be used as op outputs.+class PureResult a where+    pureResult :: ReaderT (Build OpDef) (State ResultState) a++instance PureResult (Tensor Build a) where+    pureResult = do+        ResultState i ns <- get+        put $! ResultState (i+1) ns+        makeOp <- ask+        return $ Tensor $ do+            o <- makeOp+            -- TODO: unify with BuildResult (Tensor v)+            output i <$> getOrAddOp o++instance (PureResult a1, PureResult a2) => PureResult (a1, a2) where+    pureResult = (,) <$> pureResult <*> pureResult++instance (PureResult a1, PureResult a2, PureResult a3) => PureResult (a1, a2, a3) where+    pureResult = (,,) <$> pureResult <*> pureResult <*> pureResult++instance (PureResult a1, PureResult a2, PureResult a3, PureResult a4)+         => PureResult (a1, a2, a3, a4) where+    pureResult = (,,,) <$> pureResult <*> pureResult <*> pureResult <*> pureResult++instance (PureResult a1, PureResult a2, PureResult a3, PureResult a4, PureResult a5)+         => PureResult (a1, a2, a3, a4, a5) where+    pureResult = (,,,,) <$> pureResult+                      <*> pureResult+                      <*> pureResult+                      <*> pureResult+                      <*> pureResult++instance ( PureResult a1+         , PureResult a2+         , PureResult a3+         , PureResult a4+         , PureResult a5+         , PureResult a6+         )+         => PureResult (a1, a2, a3, a4, a5, a6) where+    pureResult = (,,,,,)+               <$> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult++instance ( PureResult a1+         , PureResult a2+         , PureResult a3+         , PureResult a4+         , PureResult a5+         , PureResult a6+         , PureResult a7+         )+         => PureResult (a1, a2, a3, a4, a5, a6, a7) where+    pureResult = (,,,,,,)+               <$> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult++instance ( PureResult a1+         , PureResult a2+         , PureResult a3+         , PureResult a4+         , PureResult a5+         , PureResult a6+         , PureResult a7+         , PureResult a8+         )+         => PureResult (a1, a2, a3, a4, a5, a6, a7, a8) where+    pureResult = (,,,,,,,)+               <$> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult+               <*> pureResult++instance PureResult a => PureResult [a] where+    pureResult = do+        ResultState i ns <- get+        case ns of+            [] -> error $ "Ran out of counts in pureResult. " +++                          "Likely misuse of pureOp with output lists."+            n : rest -> do+                put $! ResultState i rest+                replicateM (fromIntegral n) pureResult++instance TensorTypes as => PureResult (TensorList Build as) where+    pureResult = loop (tensorTypes :: TensorTypeList as)+      where+        loop :: TensorTypeList bs -> ReaderT (Build OpDef) (State ResultState)+                                        (TensorList Build bs)+        loop Nil = return Nil+        loop (TensorTypeProxy :/ ls) = do+            t <- pureResult+            ts <- loop ls+            return (t :/ ts)++pureOp :: PureResult a => [Int64] -> Build OpDef -> a+pureOp sizes o = flip evalState (ResultState 0 sizes) (runReaderT pureResult o)++-----+-- Class of types that can be used as arguments++class BuildInputs a where+    buildInputs :: a -> Build [Output]++instance BuildInputs a => BuildInputs [a] where+    buildInputs = fmap concat . mapM buildInputs++instance BuildInputs (Tensor v a) where+    buildInputs (Tensor t) = do+        o <- toBuild t+        return [o]++instance BuildInputs (ListOf (Tensor v) as) where+    buildInputs Nil = return []+    buildInputs (t :/ ts) = liftM2 (++) (buildInputs t) (buildInputs ts)++----++-- | Parameters to build an op (for example, the node name or optional attributes).+-- TODO: be more type safe.+type OpParams = OpDef -> OpDef
+ src/TensorFlow/ControlFlow.hs view
@@ -0,0 +1,50 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++module TensorFlow.ControlFlow+    ( -- * Dependencies+      withControlDependencies+    , group+      -- * Operations+    , noOp+    ) where++import TensorFlow.BuildOp+import TensorFlow.Build+import TensorFlow.Nodes++-- | Modify a 'Build' action, such that all new ops rendered in it will depend+-- on the nodes in the first argument.+withControlDependencies :: (MonadBuild m, Nodes t) => t -> m a -> m a+withControlDependencies deps act = do+    nodes <- build $ getNodes deps+    withNodeDependencies nodes act++-- TODO(judahjacobson): Reimplement withDependencies.++-- | Create an op that groups multiple operations.+--+-- When this op finishes, all ops in the input @n@ have finished.  This op has+-- no output.+group :: (MonadBuild m, Nodes t) => t -> m ControlNode+group deps = withControlDependencies deps noOp++-- | Does nothing.  Only useful as a placeholder for control edges.+noOp :: MonadBuild m => m ControlNode+noOp = build $ buildOp [] $ opDef "NoOp"
+ src/TensorFlow/Core.hs view
@@ -0,0 +1,92 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE ExplicitNamespaces #-}++-- | The core functionality of TensorFlow.+--+-- Unless you are defining ops, you do not need to import other modules from+-- this package.+--+-- Basic ops are provided in the tensorflow-ops and tensorflow-core-ops+-- packages.+module TensorFlow.Core+    ( -- * Session+      Session+    , Options+    , sessionConfig+    , sessionTarget+    , sessionTracer+    , runSession+    , runSessionWithOptions+      -- ** Building graphs+    , MonadBuild(..)+      -- ** Running graphs+    , Fetchable+    , Nodes+    , run+    , run_+    , Feed+    , feed+    , runWithFeeds+    , runWithFeeds_+      -- ** Async+    , asyncProdNodes++      -- * Build+    , Build+    , BuildT+    , render+    , asGraphDef+    , addGraphDef+    , opName+    , opAttr+    , addInitializer+      -- * Tensor+    , ControlNode+    , Tensor+    , Value+    , Ref+    , value+    , tensorFromName+    , expr+      -- ** Element types+    , TensorType+    , TensorData+    , TensorDataType(decodeTensorData, encodeTensorData)+    , ResourceHandle+    , Scalar(..)+    , Shape(..)+    , OneOf+    , type (/=)++      -- * Op combinators+    , colocateWith+    , Device(..)+    , withDevice+    , withNameScope+      -- ** Dependencies+    , withControlDependencies+    , group+      -- ** Misc+    , noOp+    ) where++import TensorFlow.Build+import TensorFlow.ControlFlow+import TensorFlow.Nodes+import TensorFlow.Output+import TensorFlow.Session+import TensorFlow.Tensor+import TensorFlow.Types
+ src/TensorFlow/Internal/FFI.hs view
@@ -0,0 +1,264 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module TensorFlow.Internal.FFI+    ( TensorFlowException(..)+    , Raw.Session+    , withSession+    , extendGraph+    , run+    , TensorData(..)+    , setSessionConfig+    , setSessionTarget+    , getAllOpList+      -- * Internal helper.+    , useProtoAsVoidPtrLen+    )+    where++import Control.Concurrent.Async (Async, async, cancel, waitCatch)+import Control.Concurrent.MVar (MVar, modifyMVarMasked_, newMVar, takeMVar)+import Control.Exception (Exception, throwIO, bracket, finally, mask_)+import Control.Monad (when)+import Data.Bits (Bits, toIntegralSized)+import Data.Int (Int64)+import Data.Maybe (fromMaybe)+import Data.Typeable (Typeable)+import Data.Word (Word8)+import Foreign (Ptr, FunPtr, nullPtr, castPtr)+import Foreign.C.String (CString)+import Foreign.ForeignPtr (newForeignPtr, newForeignPtr_, withForeignPtr)+import Foreign.Marshal.Alloc (free)+import Foreign.Marshal.Array (withArrayLen, peekArray, mallocArray, copyArray)+import System.IO.Unsafe (unsafePerformIO)+import qualified Data.ByteString as B+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Encoding.Error as T+import qualified Data.Vector.Storable as S+import qualified Data.Vector.Storable.Mutable as M++import Data.ProtoLens (Message, encodeMessage)+import Proto.Tensorflow.Core.Framework.Graph (GraphDef)+import Proto.Tensorflow.Core.Framework.Types (DataType(..))+import Proto.Tensorflow.Core.Protobuf.Config (ConfigProto)++import qualified TensorFlow.Internal.Raw as Raw++data TensorFlowException = TensorFlowException Raw.Code T.Text+    deriving (Show, Eq, Typeable)++instance Exception TensorFlowException++-- | All of the data needed to represent a tensor.+data TensorData = TensorData+    { tensorDataDimensions :: [Int64]+    , tensorDataType       :: !DataType+    , tensorDataBytes      :: !(S.Vector Word8)+    }+  deriving (Show, Eq)++-- | Runs the given action after creating a session with options+-- populated by the given optionSetter.+withSession :: (Raw.SessionOptions -> IO ())+            -> ((IO () -> IO ()) -> Raw.Session -> IO a)+            -- ^ The action can spawn concurrent tasks which will+            -- be canceled before withSession returns.+            -> IO a+withSession optionSetter action = do+    drain <- newMVar []+    let cleanup s =+        -- Closes the session to nudge the pending run calls to fail and exit.+            finally (checkStatus (Raw.closeSession s)) $ do+                runners <- takeMVar drain+                -- Collects all runners before deleting the session.+                mapM_ shutDownRunner runners+                checkStatus (Raw.deleteSession s)+    bracket Raw.newSessionOptions Raw.deleteSessionOptions $ \options -> do+        optionSetter options+        bracket+            (checkStatus (Raw.newSession options))+            cleanup+            (action (asyncCollector drain))++asyncCollector :: MVar [Async ()] -> IO () -> IO ()+asyncCollector drain runner = modifyMVarMasked_ drain launchAndRecord+    where+      launchAndRecord restRunners = (: restRunners) <$> async runner++shutDownRunner :: Async () -> IO ()+shutDownRunner r = do+    cancel r+    -- TODO(gnezdo): manage exceptions better than print.+    either print (const (return ())) =<< waitCatch r++extendGraph :: Raw.Session -> GraphDef -> IO ()+extendGraph session pb =+    useProtoAsVoidPtrLen pb $ \ptr len ->+        checkStatus $ Raw.extendGraph session ptr len+++run :: Raw.Session+    -> [(B.ByteString, TensorData)] -- ^ Feeds.+    -> [B.ByteString]               -- ^ Fetches.+    -> [B.ByteString]               -- ^ Targets.+    -> IO [TensorData]+run session feeds fetches targets = do+    let nullTensor = Raw.Tensor nullPtr+    -- Use mask to avoid leaking input tensors before they are passed to 'run'+    -- and output tensors before they are passed to 'createTensorData'.+    mask_ $+        -- Feeds+        withStringArrayLen (fst <$> feeds) $ \feedsLen feedNames ->+        mapM (createRawTensor . snd) feeds >>= \feedTensors ->+        withArrayLen feedTensors $ \_ cFeedTensors ->+        -- Fetches.+        withStringArrayLen fetches $ \fetchesLen fetchNames ->+        -- tensorOuts is an array of null Tensor pointers that will be filled+        -- by the call to Raw.run.+        withArrayLen (replicate fetchesLen nullTensor) $ \_ tensorOuts ->+        -- Targets.+        withStringArrayLen targets $ \targetsLen ctargets -> do+            checkStatus $ Raw.run+                session+                nullPtr+                feedNames cFeedTensors (safeConvert feedsLen)+                fetchNames tensorOuts (safeConvert fetchesLen)+                ctargets (safeConvert targetsLen)+                nullPtr+            mapM_ Raw.deleteTensor feedTensors+            outTensors <- peekArray fetchesLen tensorOuts+            mapM createTensorData outTensors+++-- Internal.+++-- | Same as 'fromIntegral', but throws an error if conversion is "lossy".+safeConvert ::+    forall a b. (Show a, Show b, Bits a, Bits b, Integral a, Integral b)+    => a -> b+safeConvert x =+    fromMaybe+    (error ("Failed to convert " ++ show x ++ ", got " +++            show (fromIntegral x :: b)))+    (toIntegralSized x)+++-- | Use a list of ByteString as a list of CString.+withStringList :: [B.ByteString] -> ([CString] -> IO a) -> IO a+withStringList strings fn = go strings []+  where+    go [] cs = fn (reverse cs)+    -- TODO(fmayle): Is it worth using unsafeAsCString here?+    go (x:xs) cs = B.useAsCString x $ \c -> go xs (c:cs)+++-- | Use a list of ByteString as an array of CString.+withStringArrayLen :: [B.ByteString] -> (Int -> Ptr CString -> IO a) -> IO a+withStringArrayLen xs fn = withStringList xs (`withArrayLen` fn)+++-- | Create a Raw.Tensor from a TensorData.+createRawTensor :: TensorData -> IO Raw.Tensor+createRawTensor (TensorData dims dt byteVec) =+    withArrayLen (map safeConvert dims) $ \cdimsLen cdims -> do+        let len = S.length byteVec+        dest <- mallocArray len+        S.unsafeWith byteVec $ \x -> copyArray dest x len+        Raw.newTensor (toEnum $ fromEnum dt)+                      cdims (safeConvert cdimsLen)+                      (castPtr dest) (safeConvert len)+                      tensorDeallocFunPtr nullPtr++{-# NOINLINE tensorDeallocFunPtr #-}+tensorDeallocFunPtr :: FunPtr Raw.TensorDeallocFn+tensorDeallocFunPtr = unsafePerformIO $ Raw.wrapTensorDealloc $ \x _ _ -> free x++-- | Create a TensorData from a Raw.Tensor.+--+-- Takes ownership of the Raw.Tensor.+-- TODO: Currently, it just makes a copy of the Tensor (and then deletes it),+-- since the raw pointer may refer to storage inside a mutable TensorFlow+-- variable.  We should avoid that copy when it's not needed; for example,+-- by making TensorData wrap an IOVector, and changing the code that uses it.+createTensorData :: Raw.Tensor -> IO TensorData+createTensorData t = do+    -- Read dimensions.+    numDims <- Raw.numDims t+    dims <- mapM (Raw.dim t) [0..numDims-1]+    -- Read type.+    dtype <- toEnum . fromEnum <$> Raw.tensorType t+    -- Read data.+    len <- safeConvert <$> Raw.tensorByteSize t+    bytes <- castPtr <$> Raw.tensorData t :: IO (Ptr Word8)+    fp <- newForeignPtr_ bytes+    -- Make an explicit copy of the raw data, since it might point+    -- to a mutable variable's memory.+    v <- S.freeze (M.unsafeFromForeignPtr0 fp len)+    Raw.deleteTensor t+    return $ TensorData (map safeConvert dims) dtype v++-- | Runs the given action which does FFI calls updating a provided+-- status object. If the status is not OK it is thrown as+-- TensorFlowException.+checkStatus :: (Raw.Status -> IO a) -> IO a+checkStatus fn =+    bracket Raw.newStatus Raw.deleteStatus $ \status -> do+        result <- fn status+        code <- Raw.getCode status+        when (code /= Raw.TF_OK) $ do+            msg <- T.decodeUtf8With T.lenientDecode <$>+                   (Raw.message status >>= B.packCString)+            throwIO $ TensorFlowException code msg+        return result++setSessionConfig :: ConfigProto -> Raw.SessionOptions -> IO ()+setSessionConfig pb opt =+    useProtoAsVoidPtrLen pb $ \ptr len ->+        checkStatus (Raw.setConfig opt ptr len)++setSessionTarget :: B.ByteString -> Raw.SessionOptions -> IO ()+setSessionTarget target = B.useAsCString target . Raw.setTarget++-- | Serializes the given msg and provides it as (ptr,len) argument+-- to the given action.+useProtoAsVoidPtrLen :: (Message msg, Integral c, Show c, Bits c) =>+                        msg -> (Ptr b -> c -> IO a) -> IO a+useProtoAsVoidPtrLen msg f = B.useAsCStringLen (encodeMessage msg) $+        \(bytes, len) -> f (castPtr bytes) (safeConvert len)++-- | Returns the serialized OpList of all OpDefs defined in this+-- address space.+getAllOpList :: IO B.ByteString+getAllOpList = do+    foreignPtr <-+        mask_ (newForeignPtr Raw.deleteBuffer =<< checkCall)+    -- Makes a copy because it is more reliable than eviscerating+    -- Buffer to steal its memory (including custom deallocator).+    withForeignPtr foreignPtr $+        \ptr -> B.packCStringLen =<< (,)+                <$> (castPtr <$> Raw.getBufferData ptr)+                <*> (safeConvert <$> Raw.getBufferLength ptr)+    where+      checkCall = do+          p <- Raw.getAllOpList+          when (p == nullPtr) (throwIO exception)+          return p+      exception = TensorFlowException+                Raw.TF_UNKNOWN "GetAllOpList failure, check logs"
+ src/TensorFlow/Internal/Raw.chs view
@@ -0,0 +1,158 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE ForeignFunctionInterface #-}++module TensorFlow.Internal.Raw where++#include "third_party/tensorflow/c/c_api.h"++import Foreign+import Foreign.C++{#enum TF_DataType as DataType {} deriving (Show, Eq) #}+{#enum TF_Code as Code {} deriving (Show, Eq) #}+++-- Status.+{#pointer *TF_Status as Status newtype #}++newStatus :: IO Status+newStatus = {# call TF_NewStatus as ^ #}++deleteStatus :: Status -> IO ()+deleteStatus = {# call TF_DeleteStatus as ^ #}++setStatus :: Status -> Code -> CString -> IO ()+setStatus s c = {# call TF_SetStatus as ^ #} s (fromIntegral $ fromEnum c)++getCode :: Status -> IO Code+getCode s = toEnum . fromIntegral <$> {# call TF_GetCode as ^ #} s++message :: Status -> IO CString+message = {# call TF_Message as ^ #}+++-- Buffer.+data Buffer+{#pointer *TF_Buffer as BufferPtr -> Buffer #}++getBufferData :: BufferPtr -> IO (Ptr ())+getBufferData = {#get TF_Buffer->data #}++getBufferLength :: BufferPtr -> IO CULong+getBufferLength ={#get TF_Buffer->length #}++-- Tensor.+{#pointer *TF_Tensor as Tensor newtype #}++instance Storable Tensor where+    sizeOf (Tensor t) = sizeOf t+    alignment (Tensor t) = alignment t+    peek p = fmap Tensor (peek (castPtr p))+    poke p (Tensor t) = poke (castPtr p) t++-- A synonym for the int64_t type, which is used in the TensorFlow API.+-- On some platforms it's `long`; on others (e.g., Mac OS X) it's `long long`;+-- and as far as Haskell is concerned, those are distinct types (`CLong` vs+-- `CLLong`).+type CInt64 = {#type int64_t #}++newTensor :: DataType+          -> Ptr CInt64   -- dimensions array+          -> CInt         -- num dimensions+          -> Ptr ()       -- data+          -> CULong       -- data len+          -> FunPtr (Ptr () -> CULong -> Ptr () -> IO ())  -- deallocator+          -> Ptr ()       -- deallocator arg+          -> IO Tensor+newTensor dt = {# call TF_NewTensor as ^ #} (fromIntegral $ fromEnum dt)++deleteTensor :: Tensor -> IO ()+deleteTensor = {# call TF_DeleteTensor as ^ #}++tensorType :: Tensor -> IO DataType+tensorType t = toEnum . fromIntegral <$> {# call TF_TensorType as ^ #} t++numDims :: Tensor -> IO CInt+numDims = {# call TF_NumDims as ^ #}++dim :: Tensor -> CInt -> IO CInt64+dim = {# call TF_Dim as ^ #}++tensorByteSize :: Tensor -> IO CULong+tensorByteSize = {# call TF_TensorByteSize as ^ #}++tensorData :: Tensor -> IO (Ptr ())+tensorData = {# call TF_TensorData as ^ #}+++-- Session Options.+{# pointer *TF_SessionOptions as SessionOptions newtype #}++newSessionOptions :: IO SessionOptions+newSessionOptions = {# call TF_NewSessionOptions as ^ #}++setTarget :: SessionOptions -> CString -> IO ()+setTarget = {# call TF_SetTarget as ^ #}++setConfig :: SessionOptions -> Ptr () -> CULong -> Status -> IO ()+setConfig = {# call TF_SetConfig as ^ #}++deleteSessionOptions :: SessionOptions -> IO ()+deleteSessionOptions = {# call TF_DeleteSessionOptions as ^ #}+++-- Session.+{# pointer *TF_DeprecatedSession as Session newtype #}++newSession :: SessionOptions -> Status -> IO Session+newSession = {# call TF_NewDeprecatedSession as ^ #}++closeSession :: Session -> Status -> IO ()+closeSession = {# call TF_CloseDeprecatedSession as ^ #}++deleteSession :: Session -> Status -> IO ()+deleteSession = {# call TF_DeleteDeprecatedSession as ^ #}++extendGraph :: Session -> Ptr () -> CULong -> Status -> IO ()+extendGraph = {# call TF_ExtendGraph as ^ #}++run :: Session+    -> BufferPtr                          -- RunOptions proto.+    -> Ptr CString -> Ptr Tensor -> CInt  -- Input (names, tensors, count).+    -> Ptr CString -> Ptr Tensor -> CInt  -- Output (names, tensors, count).+    -> Ptr CString -> CInt                -- Target nodes (names, count).+    -> BufferPtr                          -- RunMetadata proto.+    -> Status+    -> IO ()+run = {# call TF_Run as ^ #}++-- FFI helpers.+type TensorDeallocFn = Ptr () -> CULong -> Ptr () -> IO ()+foreign import ccall "wrapper"+    wrapTensorDealloc :: TensorDeallocFn -> IO (FunPtr TensorDeallocFn)+++-- | Get the OpList of all OpDefs defined in this address space.+-- Returns a BufferPtr, ownership of which is transferred to the caller+-- (and can be freed using deleteBuffer).+--+-- The data in the buffer will be the serialized OpList proto for ops registered+-- in this address space.+getAllOpList :: IO BufferPtr+getAllOpList = {# call TF_GetAllOpList as ^ #}++foreign import ccall "&TF_DeleteBuffer"+  deleteBuffer :: FunPtr (BufferPtr -> IO ())
+ src/TensorFlow/Internal/VarInt.hs view
@@ -0,0 +1,50 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE BangPatterns #-}++{-|+Module      : TensorFlow.Internal.VarInt+Description : Encoders and decoders for varint types.++Originally taken from internal proto-lens code.+-}+module TensorFlow.Internal.VarInt+    ( getVarInt+    , putVarInt+    ) where++import Data.Attoparsec.ByteString as Parse+import Data.Bits+import Data.ByteString.Lazy.Builder as Builder+import Data.Monoid ((<>))+import Data.Word (Word64)++-- | Decode an unsigned varint.+getVarInt :: Parser Word64+getVarInt = loop 1 0+  where+    loop !s !n = do+        b <- anyWord8+        let n' = n + s * fromIntegral (b .&. 127)+        if (b .&. 128) == 0+            then return n'+            else loop (128*s) n'++-- | Encode a Word64.+putVarInt :: Word64 -> Builder+putVarInt n+    | n < 128 = Builder.word8 (fromIntegral n)+    | otherwise = Builder.word8 (fromIntegral $ n .&. 127 .|. 128)+                      <> putVarInt (n `shiftR` 7)
+ src/TensorFlow/Nodes.hs view
@@ -0,0 +1,140 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}  -- For Fetchable (TensorExpr a)+module TensorFlow.Nodes where++import Control.Applicative (liftA2, liftA3)+import Data.Functor.Identity (Identity)+import Data.Map.Strict (Map)+import Data.Monoid ((<>))+import Data.Set (Set)+import Data.Text (Text)+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set++import TensorFlow.Build+import TensorFlow.Output+import TensorFlow.Tensor+import TensorFlow.Types+import qualified TensorFlow.Internal.FFI as FFI++-- | Types that contain ops which can be run.+class Nodes t where+    getNodes :: t -> Build (Set NodeName)++-- | Types that tensor representations (e.g. 'Tensor', 'ControlNode') can be+-- fetched into.+--+-- Includes collections of tensors (e.g. tuples).+class Nodes t => Fetchable t a where+    getFetch :: t -> Build (Fetch a)++-- | Fetch action. Keeps track of what needs to be fetched and how to decode+-- the fetched data.+data Fetch a = Fetch+          { -- | Nodes to fetch+            fetches :: Set Text+            -- | Function to create an 'a' from the fetched data.+          , fetchRestore :: Map Text FFI.TensorData -> a+          }++instance Functor Fetch where+    fmap f (Fetch fetch restore) = Fetch fetch (f . restore)++instance Applicative Fetch where+    pure x = Fetch Set.empty (const x)+    Fetch fetch restore <*> Fetch fetch' restore' =+        Fetch (fetch <> fetch') (restore <*> restore')++nodesUnion :: (Monoid b, Traversable t, Applicative f) => t (f b) -> f b+nodesUnion = fmap (foldMap id) . sequenceA++instance (Nodes t1, Nodes t2) => Nodes (t1, t2) where+    getNodes (x, y) = nodesUnion [getNodes x, getNodes y]++instance (Nodes t1, Nodes t2, Nodes t3) => Nodes (t1, t2, t3) where+    getNodes (x, y, z) = nodesUnion [getNodes x, getNodes y, getNodes z]++instance (Fetchable t1 a1, Fetchable t2 a2) => Fetchable (t1, t2) (a1, a2) where+    getFetch (x, y) = liftA2 (,) <$> getFetch x <*> getFetch y++instance (Fetchable t1 a1, Fetchable t2 a2, Fetchable t3 a3)+         => Fetchable (t1, t2, t3) (a1, a2, a3) where+    getFetch (x, y, z) =+        liftA3 (,,) <$> getFetch x <*> getFetch y <*> getFetch z++instance Nodes t => Nodes [t] where+    getNodes = nodesUnion . map getNodes++instance Fetchable t a => Fetchable [t] [a] where+    getFetch ts  = sequenceA <$> mapM getFetch ts++instance Nodes ControlNode where+    getNodes (ControlNode o) = pure $ Set.singleton o++-- We use the constraint @(a ~ ())@ to help with type inference.  For example,+-- if @t :: ControlNode@, then this constraint ensures that @run t :: Session+-- ()@.  If we used @instance Fetchable ControlNode ()@ instead, then that+-- expression would be ambiguous without explicitly specifying the return type.+instance a ~ () => Fetchable ControlNode a where+    getFetch _ = return $ pure ()++instance Nodes (ListOf f '[]) where+    getNodes _ = return Set.empty++instance (Nodes (f a), Nodes (ListOf f as)) => Nodes (ListOf f (a ': as)) where+    getNodes (x :/ xs) = liftA2 Set.union (getNodes x) (getNodes xs)++instance l ~ List '[] => Fetchable (ListOf f '[]) l where+    getFetch _ = return $ pure Nil++instance (Fetchable (f t) a, Fetchable (ListOf f ts) (List as), i ~ Identity)+    => Fetchable (ListOf f (t ': ts)) (ListOf i (a ': as)) where+    getFetch (x :/ xs) = liftA2 (\y ys -> y /:/ ys) <$> getFetch x <*> getFetch xs++instance Nodes (Tensor v a) where+    getNodes (Tensor o) = Set.singleton . outputNodeName <$> toBuild o++fetchTensorVector :: forall a v . (TensorType a)+                  => Tensor v a -> Build (Fetch (TensorData a))+fetchTensorVector (Tensor o) = do+    outputName <- encodeOutput <$> toBuild o+    pure $ Fetch (Set.singleton outputName) $ \tensors ->+        let tensorData = tensors Map.! outputName+            expectedType = tensorType (undefined :: a)+            actualType = FFI.tensorDataType tensorData+            badTypeError = error $ "Bad tensor type: expected "+                                   ++ show expectedType+                                   ++ ", got "+                                   ++ show actualType+        in if expectedType /= actualType+               then badTypeError+               else TensorData tensorData++-- The constraint "a ~ a'" means that the input/output of fetch can constrain+-- the TensorType of each other.+instance (TensorType a, a ~ a') => Fetchable (Tensor v a) (TensorData a') where+    getFetch = fetchTensorVector++instance (TensorType a, TensorDataType s a, a ~ a') => Fetchable (Tensor v a) (s a') where+    getFetch t = fmap decodeTensorData <$> fetchTensorVector t
+ src/TensorFlow/Orphans.hs view
@@ -0,0 +1,46 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.+++{-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- Orphan instances for certain proto messages/enums, used internally.+-- TODO(judahjacobson): consider making proto-lens generate some or all of+-- these automatically; or, alternately, make new Haskell datatypes.+module TensorFlow.Orphans() where++import Proto.Tensorflow.Core.Framework.AttrValue+    ( AttrValue(..)+    , AttrValue'ListValue(..)+    , NameAttrList(..)+    )+import Proto.Tensorflow.Core.Framework.NodeDef+    ( NodeDef(..))+import Proto.Tensorflow.Core.Framework.ResourceHandle+    ( ResourceHandle(..))+import Proto.Tensorflow.Core.Framework.Tensor+    (TensorProto(..))+import Proto.Tensorflow.Core.Framework.TensorShape+    (TensorShapeProto(..), TensorShapeProto'Dim(..))+import Proto.Tensorflow.Core.Framework.Types (DataType(..))++deriving instance Ord AttrValue+deriving instance Ord AttrValue'ListValue+deriving instance Ord DataType+deriving instance Ord NameAttrList+deriving instance Ord NodeDef+deriving instance Ord ResourceHandle+deriving instance Ord TensorProto+deriving instance Ord TensorShapeProto+deriving instance Ord TensorShapeProto'Dim
+ src/TensorFlow/Output.hs view
@@ -0,0 +1,128 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}++module TensorFlow.Output+    ( ControlNode(..)+    , Device(..)+    -- * Ops+    , NodeName(..)+    , OpDef(..)+    , opName+    , opType+    , opAttr+    , opInputs+    , opControlInputs+    , OpType(..)+    , OutputIx(..)+    , Output(..)+    , output+    , PendingNodeName(..)+    )  where++import qualified Data.Map.Strict as Map+import Data.String (IsString(..))+import Data.Text (Text)+import qualified Data.Text as Text+import Lens.Family2 (Lens')+import Lens.Family2.Unchecked (lens)+import Proto.Tensorflow.Core.Framework.AttrValue (AttrValue(..))+import Data.Default (def)+import TensorFlow.Types (Attribute, attrLens)+import TensorFlow.Orphans ()++-- | A type of graph node which has no outputs. These nodes are+-- valuable for causing side effects when they are run.+newtype ControlNode = ControlNode { unControlNode :: NodeName }++-- | The type of op of a node in the graph.  This corresponds to the proto field+-- NodeDef.op.+newtype OpType = OpType { unOpType :: Text }+    deriving (Eq, Ord, Show)++instance IsString OpType where+    fromString = OpType . Text.pack++-- | An output of a TensorFlow node.+data Output = Output {outputIndex :: !OutputIx, outputNodeName :: !NodeName}+    deriving (Eq, Ord, Show)++output :: OutputIx -> NodeName -> Output+output = Output++newtype OutputIx = OutputIx { unOutputIx :: Int }+    deriving (Eq, Ord, Num, Enum, Show)++-- | A device that a node can be assigned to.+-- There's a naming convention where the device names+-- are constructed from job and replica names.+newtype Device = Device {deviceName :: Text}+    deriving (Eq, Ord, IsString)++instance Show Device where+    show (Device d) = show d++-- | Op definition. This corresponds somewhat to the 'NodeDef' proto.+data OpDef = OpDef+    { _opName :: !PendingNodeName+    , _opType :: !OpType+    , _opAttrs :: !(Map.Map Text AttrValue)+    , _opInputs :: [Output]+    , _opControlInputs :: [NodeName]+    }  deriving (Eq, Ord)++-- | The name specified for an unrendered Op.  If an Op has an+-- ImplicitName, it will be assigned based on the opType plus a+-- unique identifier.  Does not contain the "scope" prefix.+data PendingNodeName = ExplicitName !Text | ImplicitName+    deriving (Eq, Ord, Show)++instance IsString PendingNodeName where+    fromString = ExplicitName . fromString++-- | The name of a node in the graph.  This corresponds to the proto field+-- NodeDef.name.  Includes the scope prefix (if any) and a unique identifier+-- (if the node was implicitly named).+newtype NodeName = NodeName { unNodeName :: Text }+    deriving (Eq, Ord, Show)++opName :: Lens' OpDef PendingNodeName+opName = lens _opName (\o x -> o {_opName = x})++opType :: Lens' OpDef OpType+opType = lens _opType (\o x -> o { _opType = x})++opAttr :: Attribute a => Text -> Lens' OpDef a+opAttr n = lens _opAttrs (\o x -> o {_opAttrs = x})+              . lens (Map.findWithDefault def n) (flip (Map.insert n))+              . attrLens++opInputs :: Lens' OpDef [Output]+opInputs = lens _opInputs (\o x -> o {_opInputs = x})++opControlInputs :: Lens' OpDef [NodeName]+opControlInputs = lens _opControlInputs (\o x -> o {_opControlInputs = x})++-- TODO(gnezdo): IsString instance is weird and we should move that+-- code into a Build function+instance IsString Output where+    fromString s = case break (==':') s of+        (n, ':':ixStr) | [(ix, "" :: String)] <- read ixStr+                         -> Output (fromInteger ix) $ assigned n+        _ -> Output 0 $ assigned s+     where assigned = NodeName . Text.pack
+ src/TensorFlow/Session.hs view
@@ -0,0 +1,211 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-}++module TensorFlow.Session (+    Session,+    Options,+    sessionConfig,+    sessionTarget,+    sessionTracer,+    runSession,+    runSessionWithOptions,+    MonadBuild(..),+    extend,+    addGraphDef,+    run,+    runWithFeeds,+    run_,+    runWithFeeds_,+    asyncProdNodes,+    ) where++import Control.Monad (forever, unless, void)+import Control.Monad.Catch (MonadThrow, MonadCatch, MonadMask)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Reader (ReaderT(..), ask, asks)+import Data.ByteString (ByteString)+import Data.Default (Default, def)+import Data.Monoid ((<>))+import Data.ProtoLens (showMessage)+import Data.Set (Set)+import Data.Text.Encoding (encodeUtf8)+import Lens.Family2 (Lens', (^.), (&), (.~))+import Lens.Family2.Unchecked (lens)+import Proto.Tensorflow.Core.Framework.Graph (GraphDef, node)+import Proto.Tensorflow.Core.Protobuf.Config (ConfigProto)+import TensorFlow.Build+import TensorFlow.Nodes+import TensorFlow.Output (NodeName, unNodeName)+import TensorFlow.Tensor++import qualified Data.ByteString.Builder as Builder+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import qualified TensorFlow.Internal.FFI as FFI++-- | An action for logging.+type Tracer = Builder.Builder -> IO ()++-- Common state threaded through the session.+data SessionState+    = SessionState {+          rawSession :: FFI.Session+        , asyncCollector :: IO () -> IO ()+          -- ^ Starts the given action concurrently.+        , tracer :: Tracer+        }++newtype Session a+    = Session (ReaderT SessionState (BuildT IO) a)+    deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadCatch,+              MonadMask)++-- | Run 'Session' actions in a new TensorFlow session.+runSession :: Session a -> IO a+runSession = runSessionWithOptions def++-- | Customization for session. Use the lenses to update:+-- 'sessionTarget', 'sessionTracer', 'sessionConfig'.+data Options = Options+    { _sessionTarget :: ByteString+    , _sessionConfig :: ConfigProto+    , _sessionTracer :: Tracer+    }++instance Default Options where+    def = Options+          { _sessionTarget = ""+          , _sessionConfig = def+          , _sessionTracer = const (return ())+          }++-- | Target can be: "local", ip:port, host:port.+-- The set of supported factories depends on the linked in libraries.+sessionTarget :: Lens' Options ByteString+sessionTarget = lens _sessionTarget (\g x -> g { _sessionTarget = x })++-- | Uses the specified config for the created session.+sessionConfig :: Lens' Options ConfigProto+sessionConfig = lens _sessionConfig (\g x -> g { _sessionConfig = x })++-- | Uses the given logger to monitor session progress.+sessionTracer :: Lens' Options Tracer+sessionTracer = lens _sessionTracer (\g x -> g { _sessionTracer = x })++-- | Run 'Session' actions in a new TensorFlow session created with+-- the given option setter actions ('sessionTarget', 'sessionConfig').+runSessionWithOptions :: Options -> Session a -> IO a+runSessionWithOptions options (Session m) =+    FFI.withSession applyOptions $+        \as rs ->+            let initState = SessionState rs as (options ^. sessionTracer)+            in evalBuildT (runReaderT m initState)+  where applyOptions opt = do+            FFI.setSessionTarget (options ^. sessionTarget) opt+            FFI.setSessionConfig (options ^. sessionConfig) opt++instance MonadBuild Session where+    build = Session . lift . build++-- | Add all pending rendered nodes to the TensorFlow graph and runs+-- any pending initializers.+--+-- Note that run, runWithFeeds, etc. will all call this function implicitly.+extend :: Session ()+extend = do+    session <- Session (asks rawSession)+    trace <- Session (asks tracer)+    nodesToExtend <- build flushNodeBuffer+    unless (null nodesToExtend) $ liftIO $ do+        let graphDef = (def :: GraphDef) & node .~ nodesToExtend+        trace ("Session.extend " <> Builder.string8 (showMessage graphDef))+        FFI.extendGraph session graphDef+    -- Now that all the nodes are created, run the initializers.+    initializers <- build flushInitializers+    unless (null initializers) $+        void $ liftIO $ FFI.run session [] [] (toNodeNames initializers)++-- | Run a subgraph 't', rendering any dependent nodes that aren't already+-- rendered, and fetch the corresponding values for 'a'.+run :: Fetchable t a => t -> Session a+run = runWithFeeds []++-- | Run a subgraph 't', rendering any dependent nodes that aren't already+-- rendered, feed the given input values, and fetch the corresponding result+-- values for 'a'.+runWithFeeds :: Fetchable t a => [Feed] -> t -> Session a+runWithFeeds feeds t = do+    ns <- build $ getNodes t+    -- Note that this call to "fetch" shouldn't affect the following "extend"+    -- call, since all nodes in t and its inputs/deps will be rendered by the+    -- above call to getNodes.+    fetch <- build $ getFetch t+    runFetchWithFeeds feeds ns fetch++runFetchWithFeeds :: [Feed] -> Set NodeName -> Fetch a -> Session a+runFetchWithFeeds feeds target (Fetch fetch restore) = do+    extend+    let feeds' = fixFeeds feeds+    let fetchNames = encodeUtf8 <$> Set.toList fetch+        targetNames = toNodeNames $ Set.toList target+    session <- Session (asks rawSession)+    runResult <- liftIO $ FFI.run session+                                  feeds'+                                  fetchNames+                                  targetNames+    let resultTensorsMap = Map.fromList $ zip (Set.toList fetch) runResult+    return $ restore resultTensorsMap++toNodeNames :: [NodeName] -> [ByteString]+toNodeNames = map (encodeUtf8 . unNodeName)++-- | Run a subgraph 't', rendering and extending any dependent nodes that aren't+-- already rendered.  This behaves like 'run' except that it doesn't do any+-- fetches.+run_ :: Nodes t => t -> Session ()+run_ = runWithFeeds_ []++-- | Run a subgraph 't', rendering any dependent nodes that aren't already+-- rendered, feed the given input values, and fetch the corresponding result+-- values for 'a'.  This behaves like 'runWithFeeds' except that it doesn't do+-- any fetches.+runWithFeeds_ :: Nodes t => [Feed] -> t -> Session ()+runWithFeeds_ feeds t = do+    ns <- build $ getNodes t+    runFetchWithFeeds feeds ns (pure ())++fixFeeds :: [Feed] -> [(ByteString, FFI.TensorData)]+fixFeeds = map $ \(Feed o d) -> (encodeUtf8 $ encodeOutput o, d)++-- | Starts a concurrent thread which evaluates the given Nodes+-- forever until runSession exits or an exception occurs. Graph+-- extension happens synchronously, but the resultant run proceeds as+-- a separate thread.+asyncProdNodes :: Nodes t+                  => t  -- ^ Node to evaluate concurrently.+                  -> Session ()+asyncProdNodes nodes = do+    target <- build (getNodes nodes)+    extend+    let targetNames = toNodeNames $ Set.toList target+    state <- Session ask+    let loop = forever (void (FFI.run (rawSession state) [] [] targetNames))+    liftIO (asyncCollector state loop)
+ src/TensorFlow/Tensor.hs view
@@ -0,0 +1,193 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}  -- For the Render class++module TensorFlow.Tensor where++import Data.ByteString (ByteString)+import Data.String (IsString(..))+import qualified Data.Text as Text+import Lens.Family2 ((^.))+import Lens.Family2.State ((%=), use)++import Proto.Tensorflow.Core.Framework.NodeDef (device)+import TensorFlow.Build+import TensorFlow.Output (Output, NodeName, outputNodeName, Device(..))+import TensorFlow.Types+    ( TensorData(..)+    , ListOf(..)+    )+import qualified TensorFlow.Internal.FFI as FFI++-- | A named output of a TensorFlow operation.+--+-- The type parameter @a@ is the type of the elements in the 'Tensor'.  The+-- parameter @v@ is either:+--+--   * 'Build': An unrendered, immutable value.+--   * 'Value': A rendered, immutable value.+--   * 'Ref': A rendered stateful handle (e.g., a variable).+--+-- Note that 'expr', 'value', 'render' and 'renderValue' can help convert between+-- the different types of 'Tensor'.+data Tensor v a where+    Tensor :: TensorKind v => {tensorOutput :: v Output} -> Tensor v a++newtype Value a = Value {runValue :: a}+    deriving Functor++instance Applicative Value where+    pure = Value+    Value f <*> Value x = Value $ f x++instance Monad Value where+    f >>= g = g $ runValue f++newtype Ref a = Ref {runRef :: a}+    deriving Functor++instance Applicative Ref where+    pure = Ref+    Ref f <*> Ref x = Ref $ f x++instance Monad Ref where+    f >>= g = g $ runRef f++-- | Cast a 'Tensor Ref' into a 'Tensor Value'. This behaves like a no-op.+value :: Tensor Ref a -> Tensor Value a+value (Tensor o) = Tensor $ Value $ runRef o++renderValue :: MonadBuild m => Tensor v a -> m (Tensor Value a)+renderValue (Tensor o) = render $ Tensor $ toBuild o++-- | A pair of a 'Tensor' and some data that should be fed into that 'Tensor'+-- when running the graph.+data Feed = Feed Output FFI.TensorData++-- | A class ensuring that a given tensor is rendered, i.e., has a fixed+-- name, device, etc.+class TensorKind v => Rendered v where+    rendered :: v a -> a++instance Rendered Value where+    rendered = runValue++instance Rendered Ref where+    rendered = runRef++renderedOutput :: Rendered v => Tensor v a -> Output+renderedOutput = rendered . tensorOutput++tensorNodeName :: Rendered v => Tensor v a -> NodeName+tensorNodeName = outputNodeName . renderedOutput+++-- | Create a 'Feed' for feeding the given data into a 'Tensor' when running+-- the graph.+--+-- Note that if a 'Tensor' is rendered, its identity may change; so feeding the+-- rendered 'Tensor' may be different than feeding the original 'Tensor'.+feed :: Rendered v => Tensor v a -> TensorData a -> Feed+feed t (TensorData td) = Feed (renderedOutput t) td++-- | Create a 'Tensor' for a given name.  This can be used to reference nodes+-- in a 'GraphDef' that was loaded via 'addGraphDef'.+-- TODO(judahjacobson): add more safety checks here.+tensorFromName :: TensorKind v => Text.Text -> Tensor v a+tensorFromName = Tensor . pure . fromString . Text.unpack++-- | Like 'tensorFromName', but type-restricted to 'Value'.+tensorValueFromName :: Text.Text -> Tensor Value a+tensorValueFromName = tensorFromName++-- | Like 'tensorFromName', but type-restricted to 'Ref'.+tensorRefFromName :: Text.Text -> Tensor Ref a+tensorRefFromName = tensorFromName++type TensorList v = ListOf (Tensor v)++tensorListOutputs :: Rendered v => TensorList v as -> [Output]+tensorListOutputs Nil = []+tensorListOutputs (t :/ ts) = renderedOutput t : tensorListOutputs ts++-- | Places all nodes rendered in the given 'Build' action on the same+-- device as the given Tensor (see also 'withDevice'). Make sure that+-- the action has side effects of rendering the desired tensors. A pure+-- return would not have the desired effect.+colocateWith :: (MonadBuild m, Rendered v) => Tensor v b -> m a -> m a+colocateWith t x = do+    d <- build $ Device . (^. device)+               <$> lookupNode (outputNodeName $ renderedOutput t)+    withDevice (Just d) x+++-- | Render a 'Tensor', fixing its name, scope, device and control inputs from+-- the 'MonadBuild' context.  Also renders any dependencies of the 'Tensor' that+-- weren't already rendered.+--+-- This operation is idempotent; calling 'render' on the same input in the same+-- context will produce the same result.  However, rendering the same+-- @Tensor Build@ in two different contexts may result in two different+-- @Tensor Value@s.+render :: MonadBuild m => Tensor Build a -> m (Tensor Value a)+render (Tensor t) = Tensor . Value <$> build t++-- TODO: better name.+expr :: TensorKind v => Tensor v a -> Tensor Build a+expr (Tensor o) = Tensor $ toBuild o++-- | Records the given summary action in Build for retrieval with+-- Summary protocol buffer in string form. For safety, use the+-- pre-composed functions: Logging.scalarSummary and+-- Logging.histogramSummary.+addSummary :: (MonadBuild m, TensorKind v) => Tensor v ByteString -- ^ A 'SummaryTensor'+                        -> m ()+addSummary t = build $ do+    -- TODO: more generic way+    o <- toBuild $ tensorOutput t+    summaries %= (o :)++-- | Retrieves the summary ops collected thus far. Typically this only+-- happens once, but if 'TensorFlow.Session.buildWithSummary' is used+-- repeatedly, the values accumulate.+collectAllSummaries :: MonadBuild m => m [SummaryTensor]+collectAllSummaries = build $ map (Tensor . Value) <$> use summaries++-- | Synonym for the tensors that return serialized Summary proto.+type SummaryTensor = Tensor Value ByteString++-- | An internal class for kinds of Tensors.+class Monad v => TensorKind v where+    toBuild :: v a -> Build a++instance TensorKind Value where+    toBuild = return . rendered++instance TensorKind Ref where+    toBuild = return . rendered++instance TensorKind Build where+    toBuild = id
+ src/TensorFlow/Types.hs view
@@ -0,0 +1,539 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+-- We use UndecidableInstances for type families with recursive definitions+-- like "\\".  Those instances will terminate since each equation unwraps one+-- cons cell of a type-level list.+{-# LANGUAGE UndecidableInstances #-}++module TensorFlow.Types+    ( TensorType(..)+    , TensorData(..)+    , TensorDataType(..)+    , Scalar(..)+    , Shape(..)+    , protoShape+    , Attribute(..)+    , DataType(..)+    , ResourceHandle+    -- * Lists+    , ListOf(..)+    , List+    , (/:/)+    , TensorTypeProxy(..)+    , TensorTypes(..)+    , TensorTypeList+    , fromTensorTypeList+    , fromTensorTypes+    -- * Type constraints+    , OneOf+    , type (/=)+    , OneOfs+    -- ** Implementation of constraints+    , TypeError+    , ExcludedCase+    , NoneOf+    , type (\\)+    , Delete+    , AllTensorTypes+    ) where++import Data.Functor.Identity (Identity(..))+import Data.Complex (Complex)+import Data.Default (def)+import Data.Int (Int8, Int16, Int32, Int64)+import Data.Monoid ((<>))+import Data.Proxy (Proxy(..))+import Data.String (IsString)+import Data.Word (Word8, Word16, Word64)+import Foreign.Storable (Storable)+import GHC.Exts (Constraint, IsList(..))+import Lens.Family2 (Lens', view, (&), (.~))+import Lens.Family2.Unchecked (iso)+import Text.Printf (printf)+import qualified Data.Attoparsec.ByteString as Atto+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.ByteString.Builder (Builder)+import qualified Data.ByteString.Builder as Builder+import qualified Data.ByteString.Lazy as L+import qualified Data.Vector as V+import qualified Data.Vector.Storable as S+import Proto.Tensorflow.Core.Framework.AttrValue+    ( AttrValue(..)+    , AttrValue'ListValue(..)+    , b+    , f+    , i+    , s+    , list+    , type'+    , shape+    , tensor+    )+import Proto.Tensorflow.Core.Framework.ResourceHandle+    (ResourceHandle)+import Proto.Tensorflow.Core.Framework.Tensor as Tensor+    ( TensorProto(..)+    , boolVal+    , doubleVal+    , floatVal+    , intVal+    , int64Val+    , resourceHandleVal+    , stringVal+    , stringVal+    )+import Proto.Tensorflow.Core.Framework.TensorShape+    ( TensorShapeProto(..)+    , dim+    , size+    )+import Proto.Tensorflow.Core.Framework.Types (DataType(..))++import TensorFlow.Internal.VarInt (getVarInt, putVarInt)+import qualified TensorFlow.Internal.FFI as FFI++-- | The class of scalar types supported by tensorflow.+class TensorType a where+    tensorType :: a -> DataType+    tensorRefType :: a -> DataType+    tensorVal :: Lens' TensorProto [a]++instance TensorType Float where+    tensorType _ = DT_FLOAT+    tensorRefType _ = DT_FLOAT_REF+    tensorVal = floatVal++instance TensorType Double where+    tensorType _ = DT_DOUBLE+    tensorRefType _ = DT_DOUBLE_REF+    tensorVal = doubleVal++instance TensorType Int32 where+    tensorType _ = DT_INT32+    tensorRefType _ = DT_INT32_REF+    tensorVal = intVal++instance TensorType Int64 where+    tensorType _ = DT_INT64+    tensorRefType _ = DT_INT64_REF+    tensorVal = int64Val++integral :: Integral a => Lens' [Int32] [a]+integral = iso (fmap fromIntegral) (fmap fromIntegral)++instance TensorType Word8 where+    tensorType _ = DT_UINT8+    tensorRefType _ = DT_UINT8_REF+    tensorVal = intVal . integral++instance TensorType Word16 where+    tensorType _ = DT_UINT16+    tensorRefType _ = DT_UINT16_REF+    tensorVal = intVal . integral++instance TensorType Int16 where+    tensorType _ = DT_INT16+    tensorRefType _ = DT_INT16_REF+    tensorVal = intVal . integral++instance TensorType Int8 where+    tensorType _ = DT_INT8+    tensorRefType _ = DT_INT8_REF+    tensorVal = intVal . integral++instance TensorType ByteString where+    tensorType _ = DT_STRING+    tensorRefType _ = DT_STRING_REF+    tensorVal = stringVal++instance TensorType Bool where+    tensorType _ = DT_BOOL+    tensorRefType _ = DT_BOOL_REF+    tensorVal = boolVal++instance TensorType (Complex Float) where+    tensorType _ = DT_COMPLEX64+    tensorRefType _ = DT_COMPLEX64+    tensorVal = error "TODO (Complex Float)"++instance TensorType (Complex Double) where+    tensorType _ = DT_COMPLEX128+    tensorRefType _ = DT_COMPLEX128+    tensorVal = error "TODO (Complex Double)"++instance TensorType ResourceHandle where+    tensorType _ = DT_RESOURCE+    tensorRefType _ = DT_RESOURCE_REF+    tensorVal = resourceHandleVal++-- | Tensor data with the correct memory layout for tensorflow.+newtype TensorData a = TensorData { unTensorData :: FFI.TensorData }++-- | Types that can be converted to and from 'TensorData'.+--+-- 'S.Vector' is the most efficient to encode/decode for most element types.+class TensorType a => TensorDataType s a where+    -- | Decode the bytes of a 'TensorData' into an 's'.+    decodeTensorData :: TensorData a -> s a+    -- | Encode an 's' into a 'TensorData'.+    --+    -- The values should be in row major order, e.g.,+    --+    --   element 0:   index (0, ..., 0)+    --   element 1:   index (0, ..., 1)+    --   ...+    encodeTensorData :: Shape -> s a -> TensorData a++-- All types, besides ByteString and Bool, are encoded as simple arrays and we+-- can use Vector.Storable to encode/decode by type casting pointers.++-- TODO(fmayle): Assert that the data type matches the return type.+simpleDecode :: Storable a => TensorData a -> S.Vector a+simpleDecode = S.unsafeCast . FFI.tensorDataBytes . unTensorData++simpleEncode :: forall a . (TensorType a, Storable a)+             => Shape -> S.Vector a -> TensorData a+simpleEncode (Shape xs) v =+    if product xs /= fromIntegral (S.length v)+        then error $ printf+            "simpleEncode: bad vector length for shape %v: expected=%d got=%d"+            (show xs) (product xs) (S.length v)+        else TensorData (FFI.TensorData xs dt (S.unsafeCast v))+  where+    dt = tensorType (undefined :: a)++instance TensorDataType S.Vector Float where+    decodeTensorData = simpleDecode+    encodeTensorData = simpleEncode++instance TensorDataType S.Vector Double where+    decodeTensorData = simpleDecode+    encodeTensorData = simpleEncode++instance TensorDataType S.Vector Int8 where+    decodeTensorData = simpleDecode+    encodeTensorData = simpleEncode++instance TensorDataType S.Vector Int16 where+    decodeTensorData = simpleDecode+    encodeTensorData = simpleEncode++instance TensorDataType S.Vector Int32 where+    decodeTensorData = simpleDecode+    encodeTensorData = simpleEncode++instance TensorDataType S.Vector Int64 where+    decodeTensorData = simpleDecode+    encodeTensorData = simpleEncode++instance TensorDataType S.Vector Word8 where+    decodeTensorData = simpleDecode+    encodeTensorData = simpleEncode++instance TensorDataType S.Vector Word16 where+    decodeTensorData = simpleDecode+    encodeTensorData = simpleEncode++-- TODO: Haskell and tensorflow use different byte sizes for bools, which makes+-- encoding more expensive. It may make sense to define a custom boolean type.+instance TensorDataType S.Vector Bool where+    decodeTensorData =+        S.convert . S.map (/= 0) . FFI.tensorDataBytes . unTensorData+    encodeTensorData (Shape xs) =+        TensorData . FFI.TensorData xs DT_BOOL . S.map fromBool . S.convert+      where+        fromBool x = if x then 1 else 0 :: Word8++instance {-# OVERLAPPABLE #-} (Storable a, TensorDataType S.Vector a, TensorType a)+    => TensorDataType V.Vector a where+    decodeTensorData = (S.convert :: S.Vector a -> V.Vector a) . decodeTensorData+    encodeTensorData x = encodeTensorData x . (S.convert :: V.Vector a -> S.Vector a)++instance {-# OVERLAPPING #-} TensorDataType V.Vector (Complex Float) where+    decodeTensorData = error "TODO (Complex Float)"+    encodeTensorData = error "TODO (Complex Float)"++instance {-# OVERLAPPING #-} TensorDataType V.Vector (Complex Double) where+    decodeTensorData = error "TODO (Complex Double)"+    encodeTensorData = error "TODO (Complex Double)"++instance {-# OVERLAPPING #-} TensorDataType V.Vector ByteString where+    -- Encoded data layout (described in third_party/tensorflow/c/c_api.h):+    --   table offsets for each element :: [Word64]+    --   at each element offset:+    --     string length :: VarInt64+    --     string data   :: [Word8]+    decodeTensorData tensorData =+        either (\err -> error $ "Malformed TF_STRING tensor; " ++ err) id $+            if expected /= count+                then Left $ "decodeTensorData for ByteString count mismatch " +++                            show (expected, count)+                else V.mapM decodeString (S.convert offsets)+      where+        expected = S.length offsets+        count = fromIntegral $ product $ FFI.tensorDataDimensions+                    $ unTensorData tensorData+        bytes = FFI.tensorDataBytes $ unTensorData tensorData+        offsets = S.take count $ S.unsafeCast bytes :: S.Vector Word64+        dataBytes = B.pack $ S.toList $ S.drop (count * 8) bytes+        decodeString :: Word64 -> Either String ByteString+        decodeString offset =+            let stringDataStart = B.drop (fromIntegral offset) dataBytes+            in Atto.eitherResult $ Atto.parse stringParser stringDataStart+        stringParser :: Atto.Parser ByteString+        stringParser = getVarInt >>= Atto.take . fromIntegral+    encodeTensorData (Shape xs) vec =+        TensorData $ FFI.TensorData xs dt byteVector+      where+        dt = tensorType (undefined :: ByteString)+        -- Add a string to an offset table and data blob.+        addString :: (Builder, Builder, Word64)+                  -> ByteString+                  -> (Builder, Builder, Word64)+        addString (table, strings, offset) str =+            ( table <> Builder.word64LE offset+            , strings <> lengthBytes <> Builder.byteString str+            , offset + lengthBytesLen + strLen+            )+          where+            strLen = fromIntegral $ B.length str+            lengthBytes = putVarInt $ fromIntegral $ B.length str+            lengthBytesLen =+                fromIntegral $ L.length $ Builder.toLazyByteString lengthBytes+        -- Encode all strings.+        (table', strings', _) = V.foldl' addString (mempty, mempty, 0) vec+        -- Concat offset table with data.+        bytes = table' <> strings'+        -- Convert to Vector Word8.+        byteVector = S.fromList $ L.unpack $ Builder.toLazyByteString bytes++newtype Scalar a = Scalar {unScalar :: a}+    deriving (Show, Eq, Ord, Num, Fractional, Floating, Real, RealFloat,+              RealFrac, IsString)++instance (TensorDataType V.Vector a, TensorType a) => TensorDataType Scalar a where+    decodeTensorData = Scalar . headFromSingleton . decodeTensorData+    encodeTensorData x (Scalar y) = encodeTensorData x (V.fromList [y])++headFromSingleton :: V.Vector a -> a+headFromSingleton x+    | V.length x == 1 = V.head x+    | otherwise = error $+                  "Unable to extract singleton from tensor of length "+                  ++ show (V.length x)+++-- | Shape (dimensions) of a tensor.+newtype Shape = Shape [Int64] deriving Show++instance IsList Shape where+    type Item Shape = Int64+    fromList = Shape . fromList+    toList (Shape ss) = toList ss++protoShape :: Lens' TensorShapeProto Shape+protoShape = iso protoToShape shapeToProto+  where+    protoToShape = Shape . fmap (view size) . view dim+    shapeToProto (Shape ds) = (def :: TensorShapeProto) & dim .~ fmap (\d -> def & size .~ d) ds+++class Attribute a where+    attrLens :: Lens' AttrValue a++instance Attribute Float where+    attrLens = f++instance Attribute ByteString where+    attrLens = s++instance Attribute Int64 where+    attrLens = i++instance Attribute DataType where+    attrLens = type'++instance Attribute TensorProto where+    attrLens = tensor++instance Attribute Bool where+    attrLens = b++instance Attribute Shape where+    attrLens = shape . protoShape++-- TODO(gnezdo): support generating list(Foo) from [Foo].+instance Attribute AttrValue'ListValue where+    attrLens = list++instance Attribute [DataType] where+    attrLens = list . type'++instance Attribute [Int64] where+    attrLens = list . i++-- | A heterogeneous list type.+data ListOf f as where+    Nil :: ListOf f '[]+    (:/) :: f a -> ListOf f as -> ListOf f (a ': as)++infixr 5 :/++type family All f as :: Constraint where+    All f '[] = ()+    All f (a ': as) = (f a, All f as)++type family Map f as where+    Map f '[] = '[]+    Map f (a ': as) = f a ': Map f as++instance All Eq (Map f as) => Eq (ListOf f as) where+    Nil == Nil = True+    (x :/ xs) == (y :/ ys) = x == y && xs == ys+    -- Newer versions of GHC use the GADT to tell that the previous cases are+    -- exhaustive.+#if __GLASGOW_HASKELL__ < 800+    _ == _ = False+#endif++instance All Show (Map f as) => Show (ListOf f as) where+    showsPrec _ Nil = showString "Nil"+    showsPrec d (x :/ xs) = showParen (d > 10)+                                $ showsPrec 6 x . showString " :/ "+                                    . showsPrec 6 xs++type List = ListOf Identity++-- | Equivalent of ':/' for lists.+(/:/) :: a -> List as -> List (a ': as)+(/:/) = (:/) . Identity++infixr 5 /:/++-- | A 'Constraint' specifying the possible choices of a 'TensorType'.+--+-- We implement a 'Constraint' like @OneOf '[Double, Float] a@ by turning the+-- natural representation as a conjunction, i.e.,+--+-- @+--    a == Double || a == Float+-- @+--+-- into a disjunction like+--+-- @+--     a \/= Int32 && a \/= Int64 && a \/= ByteString && ...+-- @+--+-- using an enumeration of all the possible 'TensorType's.+type OneOf ts a+    -- Assert `TensorTypes ts` to make error messages a little better.+    = (TensorType a, TensorTypes ts, NoneOf (AllTensorTypes \\ ts) a)++type OneOfs ts as = (TensorTypes as, TensorTypes ts,+                        NoneOfs (AllTensorTypes \\ ts) as)++type family NoneOfs ts as :: Constraint where+    NoneOfs ts '[] = ()+    NoneOfs ts (a ': as) = (NoneOf ts a, NoneOfs ts as)++data TensorTypeProxy a where+    TensorTypeProxy :: TensorType a => TensorTypeProxy a++type TensorTypeList = ListOf TensorTypeProxy++fromTensorTypeList :: TensorTypeList ts -> [DataType]+fromTensorTypeList Nil = []+fromTensorTypeList ((TensorTypeProxy :: TensorTypeProxy t) :/ ts)+    = tensorType (undefined :: t) : fromTensorTypeList ts++fromTensorTypes :: forall as . TensorTypes as => Proxy as -> [DataType]+fromTensorTypes _ = fromTensorTypeList (tensorTypes :: TensorTypeList as)++class TensorTypes (ts :: [*]) where+    tensorTypes :: TensorTypeList ts++instance TensorTypes '[] where+    tensorTypes = Nil++-- | A constraint that the input is a list of 'TensorTypes'.+instance (TensorType t, TensorTypes ts) => TensorTypes (t ': ts) where+    tensorTypes = TensorTypeProxy :/ tensorTypes++-- | A constraint checking that two types are different.+type family a /= b :: Constraint where+    a /= a = TypeError a ~ ExcludedCase+    a /= b = ()++-- | Helper types to produce a reasonable type error message when the Constraint+-- "a /= a" fails.+-- TODO(judahjacobson): Use ghc-8's CustomTypeErrors for this.+data TypeError a+data ExcludedCase++-- | An enumeration of all valid 'TensorType's.+type AllTensorTypes =+    -- NOTE: This list should be kept in sync with+    -- TensorFlow.OpGen.dtTypeToHaskell.+    -- TODO: Add support for Complex Float/Double.+    '[ Float+     , Double+     , Int8+     , Int16+     , Int32+     , Int64+     , Word8+     , Word16+     , ByteString+     , Bool+     ]++-- | Removes a type from the given list of types.+type family Delete a as where+    Delete a '[] = '[]+    Delete a (a ': as) = Delete a as+    Delete a (b ': as) = b ': Delete a as++-- | Takes the difference of two lists of types.+type family as \\ bs where+    as \\ '[] = as+    as \\ (b ': bs) = Delete b as \\ bs++-- | A constraint that the type @a@ doesn't appear in the type list @ts@.+-- Assumes that @a@ and each of the elements of @ts@ are 'TensorType's.+type family NoneOf ts a :: Constraint where+    -- Specialize this type family when `ts` is a long list, to avoid deeply+    -- nested tuples of constraints.  Works around a bug in ghc-8:+    -- https://ghc.haskell.org/trac/ghc/ticket/12175+    NoneOf (t1 ': t2 ': t3 ': t4 ': ts) a+        = (a /= t1, a /= t2, a /= t3, a /= t4, NoneOf ts a)+    NoneOf (t1 ': t2 ': t3 ': ts) a = (a /= t1, a /= t2, a /= t3, NoneOf ts a)+    NoneOf (t1 ': t2 ': ts) a = (a /= t1, a /= t2, NoneOf ts a)+    NoneOf (t1 ': ts) a = (a /= t1, NoneOf ts a)+    NoneOf '[] a = ()
+ tensorflow.cabal view
@@ -0,0 +1,94 @@+name:                tensorflow+version:             0.1.0.0+synopsis:            TensorFlow bindings.+description:+    This library provides an interface to the TensorFlow+    bindings.  "TensorFlow.Core" contains the base API for+    building and running computational graphs.  Other packages+    such as @tensorflow-ops@ contain bindings to the actual+    computational kernels.+    .+    For more documentation and examples, see+    <https://github.com/tensorflow/haskell#readme>+homepage:            https://github.com/tensorflow/haskell#readme+license:             Apache+license-file:        LICENSE+author:              TensorFlow authors+maintainer:          tensorflow-haskell@googlegroups.com+copyright:           Google Inc.+category:            Machine Learning+build-type:          Simple+cabal-version:       >=1.22++library+  hs-source-dirs:   src+  exposed-modules:  TensorFlow.Build+                  , TensorFlow.BuildOp+                  , TensorFlow.ControlFlow+                  , TensorFlow.Core+                  , TensorFlow.Internal.FFI+                  , TensorFlow.Internal.VarInt+                  , TensorFlow.Nodes+                  , TensorFlow.Output+                  , TensorFlow.Session+                  , TensorFlow.Tensor+                  , TensorFlow.Types+  other-modules:    TensorFlow.Internal.Raw+                  , TensorFlow.Orphans+  build-tools:      c2hs+  build-depends:  proto-lens == 0.2.*+                -- Used by the custom Setup script (for the test-suite).+                , proto-lens-protoc == 0.2.*+                , tensorflow-proto == 0.1.*+                , base >= 4.7 && < 5+                , async+                , attoparsec+                , bytestring+                , containers+                , data-default+                , exceptions+                , fgl+                , lens-family+                , mainland-pretty+                , mtl+                , semigroups+                , split+                , text+                , temporary+                , transformers+                , vector+  extra-libraries:     tensorflow+  default-language:    Haskell2010+  include-dirs: .++Test-Suite FFITest+  default-language: Haskell2010+  type: exitcode-stdio-1.0+  main-is: FFITest.hs+  hs-source-dirs: tests+  build-depends: HUnit+               , base+               , bytestring+               , lens-family+               , proto-lens+               , tensorflow+               , tensorflow-proto+               , test-framework+               , test-framework-hunit+++Test-Suite VarIntTest+  default-language: Haskell2010+  type: exitcode-stdio-1.0+  main-is: VarIntTest.hs+  hs-source-dirs: tests+  build-depends: base+               , attoparsec+               , bytestring+               , tensorflow+               , test-framework+               , test-framework-quickcheck2++source-repository head+  type:     git+  location: https://github.com/tensorflow/haskell
+ tests/FFITest.hs view
@@ -0,0 +1,39 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++-- | Tests for FFI.++module Main where++import Data.ProtoLens (decodeMessage)+import Lens.Family2 (view)+import TensorFlow.Internal.FFI (getAllOpList)+import Test.HUnit (assertBool, assertFailure)+import Test.Framework (defaultMain)+import Test.Framework.Providers.HUnit (testCase)+import Proto.Tensorflow.Core.Framework.OpDef (OpList, op)++testParseAll :: IO ()+testParseAll = do+    opList <- getAllOpList+    either+        assertFailure+        (assertBool "Expected non-empty list of default Ops"+         . not . null . view op)+        (decodeMessage opList :: Either String OpList)++main :: IO ()+main = defaultMain+    [ testCase "ParseAllOps" testParseAll+    ]
+ tests/VarIntTest.hs view
@@ -0,0 +1,33 @@+-- Copyright 2016 TensorFlow authors.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++module Main where++import Data.ByteString.Builder (toLazyByteString)+import Test.Framework (defaultMain, Test)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import qualified Data.Attoparsec.ByteString.Lazy as Atto++import TensorFlow.Internal.VarInt++testEncodeDecode :: Test+testEncodeDecode = testProperty "testEncodeDecode" $ \x ->+    let bytes = toLazyByteString (putVarInt x)+    in case Atto.eitherResult $ Atto.parse getVarInt bytes of+        Left _ -> False+        Right y -> x == y++main :: IO ()+main = defaultMain [ testEncodeDecode+                   ]