Raw File
localmpo.h
//
// Copyright 2018 The Simons Foundation, Inc. - All Rights Reserved.
//
// 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.
//
#ifndef __ITENSOR_LOCALMPO
#define __ITENSOR_LOCALMPO
#include "itensor/mps/mpo.h"
#include "itensor/mps/localop.h"
#include "itensor/util/print_macro.h"

namespace itensor {

//
// The LocalMPO class projects an MPO 
// into the reduced Hilbert space of
// some number of sites of an MPS.
// (The default is 2 sites.)
//
//   .----...---                ----...--.
//   |  |     |      |      |     |      | 
//   W1-W2-..Wj-1 - Wj - Wj+1 -- Wj+2..-WN
//   |  |     |      |      |     |      | 
//   '----...---                ----...--'
//
// 
//  Here the W's are the site tensors
//  of the MPO "Op" and the method position(j,psi)
//  has been called using the MPS 'psi' as a basis 
//  for the projection.
//
//  This results in an unprojected region of
//  num_center sites starting at site j.
//

class LocalMPO
    {
    public:

    //
    // Constructors
    //

    LocalMPO();

    //
    //Regular case where H is an MPO for a finite system
    //
    LocalMPO(MPO const& H, 
             Args const& args = Args::global());

    //
    //Use an MPS instead of an MPO. Equivalent to using an MPO
    //of the outer product |Psi><Psi| but much more efficient.
    //
    LocalMPO(MPS const& Psi, 
             Args const& args = Args::global());

    //
    //Use an MPO having boundary indices capped off by left and
    //right boundary tensors LH and RH. Ok if one or both boundary 
    //tensors are default-constructed.
    //
    LocalMPO(const MPO& H, 
             const ITensor& LH, 
             const ITensor& RH,
             const Args& args = Args::global());

    //
    //Use an MPS with boundary indices capped off by left and right
    //boundary tensors LP and RP. Ok if one or both boundary tensors 
    //are default-constructed.
    //
    LocalMPO(const MPS& Psi, 
             const ITensor& LP,
             const ITensor& RP,
             const Args& args = Args::global());

    //
    //Use an MPO having boundary indices capped off by left and
    //right boundary tensors LH and RH at positions LHlim and RHlim
    //These positions indicate the site number of the right-most MPO
    //tensor included in LH and the left-most MPO tensor included in RH.
    //
    LocalMPO(MPO const& H, 
             ITensor const& LH, 
             int LHlim,
             ITensor const& RH,
             int RHlim,
             Args const& args = Args::global());

    //
    // Sparse Matrix Methods
    //

    void
    product(const ITensor& phi, ITensor& phip) const;

    Real
    expect(const ITensor& phi) const { return lop_.expect(phi); }

    ITensor
    deltaRho(const ITensor& AA, 
             const ITensor& comb, Direction dir) const
        { return lop_.deltaRho(AA,comb,dir); }

    ITensor
    diag() const { return lop_.diag(); }

    //
    // position(b,psi) uses the MPS psi
    // to adjust the edge tensors such
    // that the MPO tensors at positions
    // b and b+1 are exposed
    //
    void
    position(int b, MPS const& psi);

    int
    position() const;

    void
    shift(int j, Direction dir, ITensor const& A);

    //
    // Accessor Methods
    //

    void
    reset()
        {
        LHlim_ = 0;
        RHlim_ = Op_->length()+1;
        }

    ITensor const&
    L() const { return PH_[LHlim_]; }
    // Replace left edge tensor at current bond
    void
    L(ITensor const& nL) { PH_[LHlim_] = nL; }
    // Replace left edge tensor bordering site j
    // (so that nL includes sites < j)
    void
    L(int j, ITensor const& nL);


    ITensor const&
    R() const { return PH_[RHlim_]; }
    // Replace right edge tensor at current bond
    void
    R(ITensor const& nR) { PH_[RHlim_] = nR; }
    // Replace right edge tensor bordering site j
    // (so that nR includes sites > j)
    void
    R(int j, ITensor const& nR);

    const MPO&
    H() const 
        { 
        if(Op_ == 0)
            Error("LocalMPO is null or contains an MPS");
        return *Op_;
        }

    int
    numCenter() const { return nc_; }
    void
    numCenter(int val) 
        { 
        if(val < 1) Error("numCenter must be set >= 1");
        nc_ = val; 
        }

    size_t
    size() const { return lop_.size(); }

    explicit operator bool() const { return Op_ != 0 || Psi_ != 0; }

    bool
    doWrite() const { return do_write_; }
    void
    doWrite(bool val,
            Args const& args = Args::global()) 
        { 
        if(Psi_ != 0) Error("Write to disk not yet supported for LocalMPO initialized with an MPS");
        if(!do_write_ && (val == true))
            {
            initWrite(args); 
            }
        do_write_ = val; 
        }

    std::string const&
    writeDir() const { return writedir_; }

    int
    leftLim() const { return LHlim_; }

    int
    rightLim() const { return RHlim_; }

    private:

    /////////////////
    //
    // Data Members
    //

    const MPO* Op_;
    std::vector<ITensor> PH_;
    int LHlim_,RHlim_;
    int nc_;

    LocalOp lop_;

    bool do_write_ = false;
    std::string writedir_ = "./";

    const MPS* Psi_;

    //
    /////////////////

    void
    makeL(const MPS& psi, int k);

    void
    makeR(const MPS& psi, int k);

    void
    setLHlim(int val);

    void
    setRHlim(int val);

    void
    initWrite(Args const& args);

    std::string
    PHFName(int j) const
        {
        return format("%s/PH_%03d",writedir_,j);
        }

    };

inline LocalMPO::
LocalMPO()
    : Op_(0),
      LHlim_(-1),
      RHlim_(-1),
      nc_(2),
      Psi_(0)
    { }

inline LocalMPO::
LocalMPO(const MPO& H, 
         const Args& args)
    : Op_(&H),
      PH_(H.length()+2),
      LHlim_(0),
      RHlim_(H.length()+1),
      nc_(2),
      Psi_(0)
    { 
    if(args.defined("NumCenter"))
        numCenter(args.getInt("NumCenter"));
    }

inline LocalMPO::
LocalMPO(const MPS& Psi, 
         const Args& args)
    : Op_(0),
      PH_(Psi.length()+2),
      LHlim_(0),
      RHlim_(Psi.length()+1),
      nc_(2),
      Psi_(&Psi)
    { 
    if(args.defined("NumCenter"))
        numCenter(args.getInt("NumCenter"));
    }

inline LocalMPO::
LocalMPO(const MPO& H, 
         const ITensor& LH, const ITensor& RH,
         const Args& args)
    : Op_(&H),
      PH_(H.length()+2),
      LHlim_(0),
      RHlim_(H.length()+1),
      nc_(2),
      Psi_(0)
    { 
    PH_[0] = LH;
    PH_[H.length()+1] = RH;
    if(H.length()==2)
        lop_.update(Op_->A(1),Op_->A(2),L(),R());
    if(args.defined("NumCenter"))
        numCenter(args.getInt("NumCenter"));
    }

inline LocalMPO::
LocalMPO(MPS const& Psi, 
         ITensor const& LP,
         ITensor const& RP,
         Args const& args)
    : Op_(0),
      PH_(Psi.length()+2),
      LHlim_(0),
      RHlim_(Psi.length()+1),
      nc_(2),
      Psi_(&Psi)
    { 
    PH_[0] = LP;
    PH_[Psi.length()+1] = RP;
    if(args.defined("NumCenter"))
        numCenter(args.getInt("NumCenter"));
    }

inline LocalMPO::
LocalMPO(MPO const& H, 
         ITensor const& LH, 
         int LHlim,
         ITensor const& RH,
         int RHlim,
         Args const& args)
    : Op_(&H),
      PH_(H.length()+2),
      LHlim_(LHlim),
      RHlim_(RHlim),
      nc_(2),
      Psi_(0)
    { 
    PH_.at(LHlim) = LH;
    PH_.at(RHlim) = RH;
    if(H.length()==2) lop_.update(Op_->A(1),Op_->A(2),L(),R());
    if(args.defined("NumCenter")) numCenter(args.getInt("NumCenter"));
    }

void inline LocalMPO::
product(ITensor const& phi, 
        ITensor& phip) const
    {
    if(Op_ != 0)
        {
        lop_.product(phi,phip);
        }
    else 
    if(Psi_ != 0)
        {
        int b = position();
        auto othr = (!L() ? dag(prime(Psi_->A(b),"Link")) : L()*dag(prime(Psi_->A(b),"Link")));
        auto othrR = (!R() ? dag(prime(Psi_->A(b+1),"Link")) : R()*dag(prime(Psi_->A(b+1),"Link")));
        othr *= othrR;
        auto z = (othr*phi).eltC();

        phip = dag(othr);
        phip *= z;
        }
    else
        {
        Error("LocalMPO is null");
        }
    }

void inline LocalMPO::
L(int j, ITensor const& nL)
    {
    if(LHlim_ > j-1) setLHlim(j-1);
    PH_[LHlim_] = nL;
    }

void inline LocalMPO::
R(int j, ITensor const& nR)
    {
    if(RHlim_ < j+1) setRHlim(j+1);
    PH_[RHlim_] = nR;
    }

inline void LocalMPO::
position(int b, MPS const& psi)
    {
    if(!(*this)) Error("LocalMPO is null");

    makeL(psi,b-1);
    makeR(psi,b+nc_);

    setLHlim(b-1); //not redundant since LHlim_ could be > b-1
    setRHlim(b+nc_); //not redundant since RHlim_ could be < b+nc_

#ifdef DEBUG
    if(nc_ != 2)
        {
        Error("LocalOp only supports 2 center sites currently");
        }
#endif

    if(Op_ != 0) //normal MPO case
        {
        lop_.update(Op_->A(b),Op_->A(b+1),L(),R());
        }
    }

int inline LocalMPO::
position() const
    {
    if(RHlim_-LHlim_ != (nc_+1))
        {
        throw ITError("LocalMPO position not set");
        }
    return LHlim_+1;
    }

inline void LocalMPO::
shift(int j, 
      Direction dir, 
      ITensor const& A)
    {
    if(!(*this)) Error("LocalMPO is null");

#ifdef DEBUG
    if(nc_ != 2)
        {
        Error("LocalOp only supports 2 center sites currently");
        }
#endif

    if(dir == Fromleft)
        {
        if((j-1) != LHlim_)
            {
            std::cout << "j-1 = " << (j-1) << ", LHlim = " << LHlim_ << std::endl;
            Error("Can only shift at LHlim");
            }
        auto& E = PH_.at(LHlim_);
        auto& nE = PH_.at(j);
        nE = E * A;
        nE *= Op_->A(j);
        nE *= dag(prime(A));
        setLHlim(j);
        setRHlim(j+nc_+1);

        lop_.update(Op_->A(j+1),Op_->A(j+2),L(),R());
        }
    else //dir == Fromright
        {
        if((j+1) != LHlim_)
            {
            std::cout << "j+1 = " << (j+1) << ", RHlim_ = " << RHlim_ << std::endl;
            Error("Can only shift at RHlim_");
            }
        auto& E = PH_.at(RHlim_);
        auto& nE = PH_.at(j);
        nE = E * A;
        nE *= Op_->A(j);
        nE *= dag(prime(A));
        setLHlim(j-nc_-1);
        setRHlim(j);

        lop_.update(Op_->A(j-1),Op_->A(j),L(),R());
        }
    }

inline void LocalMPO::
makeL(MPS const& psi, int k)
    {
    if(!PH_.empty())
        {
        if(Op_ == 0) //Op is actually an MPS
            {
            while(LHlim_ < k)
                {
                auto ll = LHlim_;
                PH_.at(ll+1) = (!PH_.at(ll) ? psi(ll+1) : PH_[ll]*psi(ll+1));
                PH_[ll+1] *= dag(prime(Psi_->A(ll+1),"Link"));
                setLHlim(ll+1);
                }
            }
        else //normal MPO case
            {
            while(LHlim_ < k)
                {
                auto ll = LHlim_;
                if(PH_.at(ll))
                    {
                    PH_.at(ll+1) = PH_.at(ll)*psi(ll+1);
                    }
                else
                    {
                    PH_.at(ll+1) = psi(ll+1);
                    }
                PH_.at(ll+1) *= Op_->A(ll+1);
                PH_.at(ll+1) *= dag(prime(psi(ll+1)));
                setLHlim(ll+1);
                }
            }
        }
    }

inline void LocalMPO::
makeR(MPS const& psi, int k)
    {
    if(!PH_.empty())
        {
        if(Op_ == 0) //Op is actually an MPS
            {
            while(RHlim_ > k)
                {
                const int rl = RHlim_;
                PH_.at(rl-1) = (!PH_.at(rl) ? psi(rl-1) : PH_[rl]*psi(rl-1));
                PH_[rl-1] *= dag(prime(Psi_->A(rl-1),"Link"));
                setRHlim(rl-1);
                }
            }
        else //normal MPO case
            {
            while(RHlim_ > k)
                {
                auto rl = RHlim_;
                //printfln(" Making environment with rl=%d (using H[%d])",rl,rl-1);
                //Print(PH_.at(rl));
                //Print(Op_->A(rl-1));
                //Print(psi(rl-1));
                if(PH_.at(rl))
                    {
                    PH_.at(rl-1) = PH_.at(rl)*psi(rl-1);
                    }
                else
                    {
                    PH_.at(rl-1) = psi(rl-1);
                    }
                PH_.at(rl-1) *= Op_->A(rl-1);
                PH_.at(rl-1) *= dag(prime(psi(rl-1)));
                //printfln("PH[%d] = \n%s",rl-1,PH_.at(rl-1));
                //PAUSE
                setRHlim(rl-1);
                }
            }
        }
    }

void inline LocalMPO::
setLHlim(int val)
    {
    if(!do_write_)
        {
        LHlim_ = val;
        return;
        }

    if(LHlim_ != val && PH_.at(LHlim_))
        {
        writeToFile(PHFName(LHlim_),PH_.at(LHlim_));
        PH_.at(LHlim_) = ITensor();
        }
    LHlim_ = val;
    if(LHlim_ < 1) 
        {
        //Set to null tensor and return
        PH_.at(LHlim_) = ITensor();
        return;
        }
    if(!PH_.at(LHlim_))
        {
        std::string fname = PHFName(LHlim_);
        readFromFile(fname,PH_.at(LHlim_));
        }
    }

void inline LocalMPO::
setRHlim(int val)
    {
    if(!do_write_)
        {
        RHlim_ = val;
        return;
        }

    if(RHlim_ != val && PH_.at(RHlim_))
        {
        writeToFile(PHFName(RHlim_),PH_.at(RHlim_));
        PH_.at(RHlim_) = ITensor();
        }
    RHlim_ = val;
    if(RHlim_ > Op_->length()) 
        {
        //Set to null tensor and return
        PH_.at(RHlim_) = ITensor();
        return;
        }
    if(!PH_.at(RHlim_))
        {
        std::string fname = PHFName(RHlim_);
        readFromFile(fname,PH_.at(RHlim_));
        }
    }

void inline LocalMPO::
initWrite(Args const& args)
    {
    auto basedir = args.getString("WriteDir","./");
    writedir_ = mkTempDir("PH",basedir);
    }

} //namespace itensor


#endif
back to top